如何使用 sed、grep 或 awk 根据另一个文件中的行号保留文件中的某些行

M.A*_*A.G 5 grep sed awk text-processing

我有两个文件。File1包含一些句子,并File2包含我想要保留的行号File1

例如,File1

He is a boy.
She is a cook.
Okay.
She went to school.
She is pretty.
Run Code Online (Sandbox Code Playgroud)

File2:

1
4
Run Code Online (Sandbox Code Playgroud)

输出:

He is a boy.
She went to school.
Run Code Online (Sandbox Code Playgroud)

有没有办法可以使用sed, grep, 或 来做到这一点awk?我不想像这里一样手动编写行号。

Kus*_*nda 12

我们可以将数字列表转换为命令序列,并在一次调用中将sed它们作为编辑脚本运行:sedsed

sed 's/$/p/' lines.list | sed -n -f /dev/stdin file.txt
Run Code Online (Sandbox Code Playgroud)

在这里,第一个sed创建一个由诸如等sed命令组成的脚本,只需在每行末尾插入即可。然后,该脚本被发送到管道之后的第二个,该管道读取该脚本并将其与文本文件作为输入一起应用。1p4ppsed-f /dev/stdin

这需要每个文件只读取一次。


使用awk,将行号作为键读入关联数组,然后在读取另一个文件时,查看当前行号是否是之前在数组中作为键的行号之一:

awk 'FNR == NR { lines[$0]; next } (FNR in lines)' lines.list file.txt
Run Code Online (Sandbox Code Playgroud)

在 中awk,特殊变量NRFNR分别是迄今为止读取的记录(行)总数和当前文件中读取的记录(行)总数。如果NR等于FNR,我们将从第一个输入文件中读取,并使用当前行 ,作为键(未给出值)创建一个数组条目$0,并立即跳到下一行输入。

如果我们不是从当前行读取,我们会进行测试,FNR in lines看看FNR当前文件中的行号是否是名为 的数组中的键lines。如果是,则将打印当前行。


如果没有其他工具的大力支持,该grep实用程序并不是真正用于执行此类任务的。它从内容与给定模式匹配(或不匹配)的文本文件中提取行。因此,模式应该与行匹配,而不是行号。

以下内容仅供娱乐,不应被视为如何实际解决此问题的建议。

您可以使用以下命令插入行号grep

grep -n '.*' file.txt
Run Code Online (Sandbox Code Playgroud)

这将在文件中所有行的开头插入行号,紧接着是:该行的原始内容。

然后,与sed解决方案一样,我们可以修改模式文件以使其与这些特定数字的选择相匹配:

sed 's/.*/^&:/' lines.list
Run Code Online (Sandbox Code Playgroud)

这将输出正则表达式,例如^1:^4:,每个正则表达式与行开头的特定行号匹配。

然后我们可以grep使用这些表达式(这里借助过程替换)。最后,我们使用以下命令删除临时行号cut

grep -n '.*' file.txt | grep -f <(sed 's/.*/^&:/' lines.list) | cut -d : -f 2-
Run Code Online (Sandbox Code Playgroud)

...但这太做作了,甚至不能被认为是一个合理的解决方案。


上述每个解决方案将始终按照所选行在文本文件中出现的顺序显示它们。如果您想按照行号文件中出现的顺序输出行,那么您可以使用sed(或awk,请参阅下文):

sed 's/$/p/' lines.list | ed -s file.txt
Run Code Online (Sandbox Code Playgroud)

p再次,我们通过简单地在每行末尾添加来从行号文件创建编辑脚本。

然后,该脚本作为命令输入传递到ed编辑器,编辑器将命令按顺序应用于文本文件。

测试:

$ cat lines.list
4
1
Run Code Online (Sandbox Code Playgroud)
$ sed 's/$/p/' lines.list | ed -s file.txt
She went to school.
He is a boy.
Run Code Online (Sandbox Code Playgroud)

请注意,sed将整个文件读入内存,就像下面的等效awk程序一样:

awk 'NR == FNR { lines[FNR] = $0; next } { print lines[$0] }' file.txt lines.list
Run Code Online (Sandbox Code Playgroud)

请注意,与之前的解决方案相比,输入文件已切换awk。这允许我们首先将文本文件逐行读入数组lines,然后在读取带有行号的文件时随机从中选择行。

  • @EdMorton 由于问题中用户的“sed、grep 或 awk”列表,我给出了各种答案。我同意单个“awk”调用可能是最有用的。 (2认同)