Bash:按行号过滤行

Bash:按行号过滤行

如果我有一个包含许多行和列的分隔文件 ( data.txt):

346 dfd asw  34
565 sd  wdew 34
667 ffg wew  23
473 sa  as   21
533 jhf qwe  54

以及另一个包含我要提取的行号的文件 ( positions.txt)

3
5
8

如何使用该positions.txt文件从中提取这些位置data.txt?这是我期望的示例结果:

667 ffg wew  23
533 jhf qwe  54

答案1

简单地与awk:

awk 'NR==FNR{ pos[$1]; next }FNR in pos' positions.txt data.txt
  • NR==FNR{ ... }- 处理第一个输入文件(即positions.txt):
    • pos[$1]- 累积位置(记录数)设置为pos数组键
    • next- 跳转到下一条记录
  • FNR in pos- 处理第二个输入文件时data.txtFNR指示已从当前输入文件读取了多少条记录)。仅当当前记录号FNR位于位置数组中时才打印记录pos(按键搜索)

示例输出:

667 ffg wew  23
533 jhf qwe  54
...

答案2

首先sedpositions.txt文件创建一个脚本:

sed 's/$/p/' positions.txt

这将输出

3p
5p
8p

这个简单的脚本只会打印指定的行。

然后将其应用到data.txt文件中。如果您正在使用bash(或任何能够理解进程替换的 shell <( ... )):

sed -n -f <( sed 's/$/p/' positions.txt ) data.txt

除了给定脚本显式打印的内容之外,-n停止输出任何内容。sedsed

根据给出的例子,这将产生

667 ffg wew  23
533 jhf qwe  54

如果不使用bash,那么

sed 's/$/p/' positions.txt >filter.sed
sed -n -f filter.sed data.txt
rm -f filter.sed

...也会做同样的事情。

答案3

如果positions.txt已排序,则也可以在一次遍历两个文件的情况下执行此操作,而无需positions.txt完整存储。positions.txt当满足上一个匹配行时,只需读取下一行:

$ awk -vpos=positions.txt 'function get() { getline num < pos } 
     BEGIN { get() } NR==num { print; get() }' data.txt                 
667 ffg wew  23
533 jhf qwe  54

实际上,这仅在两个文件都非常大或者您确实,真的内存不足。

答案4

我们可以通过简单的 for 循环来做到这一点

方法1使用sed和for循环

for i in `cat positions.txt`; do sed -n ""$i"p" data.txt ; done

输出

667 ffg wew  23
533 jhf qwe  54

方法2使用awk和for循环

for i in `cat positions.txt`; do awk -v i="$i" 'NR==i {print $0}' data.txt ;done

输出

667 ffg wew  23
533 jhf qwe  54

相关内容