【问题标题】:Print selected lines by number in unix在unix中按数字打印选定的行
【发布时间】:2020-07-01 05:50:48
【问题描述】:

我可以将行(118 到 122)的范围打印为:

sed -n '118,122p;16483q' large_text_file

但是如何在 unix 中打印第 188、144 和 154 行(以相同的顺序)?

【问题讨论】:

  • 应该如何处理输入文件中不存在的行号?多次指定的同一行号应该如何处理?例如给定一个由seq 5 生成的 5 行输入文件,cmd 4 2 17 2 的输出应该是您要创建的cmd 吗?

标签: linux bash shell awk sed


【解决方案1】:

对于以指定顺序打印大文件的行awk会更合适:

# prepare sample input data
printf 'line # %s\n' {1..500} > file

# run awk
awk -v nums='188 144 154' '
BEGIN {
   tot = n = split(nums, arr)       # split and store numbers in an array
   for (i=1; i<=n; i++)             # convert to lines array with key as numbers
      lines[arr[i]]
}
NR in lines {                       # if current line no is in lines array
   lines[NR] = $0                   # store current line as value
   if (!--tot)                      # decrement total count
      exit                          # exit when we have processed all 
}
END {                               # print all stored lines
   for (i=1; i<=n; i++)
      print lines[arr[i]]
}' file

输出:

line # 188
line # 144
line # 154

【讨论】:

  • 出于好奇,在我对 7 MB 文件的测试中,awk 解决方案的运行速度比 ed 解决方案快 4 倍。
  • 您可以通过将tot 比较移动到NR in lines 块内来提高效率,因为这是tot 更改的唯一地方:if (!--tot) exit。评论应该说递减,顺便说一句,而不是递增。我想知道是否有必要在 END 打印之前测试 arr[i] in lines 以处理 nums 数字大于输入中的行数的情况? OP 没有说明如何处理,也没有说明如何处理 nums 中的重复行号。
  • 不客气。我无法想象这是怎么可能的,尽管如果你有一个 1,000 行的输入文件并想要打印 3 行,那么它现在的位置将被测试 1,000 次,而如果你将它移动到块内它只会是测试了 3 次。哦,好吧...
  • 当我说“永不归零”时,我的意思当然是在测试条件时 - 在测试之后会变为零,但为时已晚。 FWIW 我刚刚在 MacOS 上使用 GNU awk 和 BSD awk 测试了这两种方法,在第三次运行时间上,使用 if (!--tot) exit 和使用 nums='188 144 154000' 和 500,000 行输入文件的两种 awk 略有加快。
  • 对,会有更大的时差,具体取决于您要匹配的行号数量以及它们在输入文件中的距离,但归根结底,这只是一个比较,所以它是不会有很大的不同。
【解决方案2】:

你也可以使用ed:

printf "%s\n" 188p 144p 154p | ed -s large_text_file

虽然它的适用性确实取决于文件的大小


如 cmets 中所述,如果您只有一个地址,ed 假定使用 print 命令。因此,如果您要打印的行号在 bash 数组中,使用它而不是直接在命令中硬编码数字变得微不足道:

declare -a lines=(188 144 154)
printf "%s\n" "${lines[@]}" | ed -s large_text_file

【讨论】:

  • 在没有p 的情况下工作,至少在这一边。
  • @Jetchisel 是的,只是一个地址有一个隐含的p。虽然我喜欢明确...但是想想看,如果行号在 bash 数组中,那么使用 printf "%s\n" "${foo[@]}" 会很好。
【解决方案3】:

对于这个任务awk 很可能是最好的,但如果你想使用sed 其他原因,试试sed -n '144h; 154H; 188{p; g; p;}' large_text_file

【讨论】:

    【解决方案4】:

    这可能对你有用(GNU sed、printf、排序):

    printf "%s\n" 90p 90q 1,5p |
    cat -n |
    sed -E 's/(.*\t)(.*)p/\2{h;s#^#\1#p;g}/;s/(.*\t)(.*)q/\2q/' |
    sed -nf - file |
    sort -ns |
    sed 's/[^\t]*\t//'
    

    在单独的行上打印每个行号。命令集是pq

    为命令添加排名索引。

    创建一个 sed 脚本并在要打印的每一行前面插入排名索引。

    阅读file 并打印行或退出,无论排名指数如何。

    按排名指数排序,保持原顺序。

    打印删除排名索引的结果。

    注意一旦在file 中达到退出q 行号,就不会执行file 的进一步处理。只允许使用一个q 命令。

    【讨论】:

      猜你喜欢
      • 2013-07-12
      • 1970-01-01
      • 2011-03-20
      • 1970-01-01
      • 1970-01-01
      • 2016-11-11
      • 2011-10-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多