grep 文件中的所有行并将行从模式匹配点写入文件

grep 文件中的所有行并将行从模式匹配点写入文件

例如,一个temp.txt文件包含如下信息:

adsf on line jhkjhvjdbvjvbvbdjkvn  
qerwtt on line fdgdgdgdd  
qwqertg on line safffasffaf  
wrt on line adaddsd

我想on line在文件的所有行中进行 grep 并将行的剩余部分写入另一个文件,即在temp.txt文件处理之后,新文件应包含:

on line jhkjhvjdbvjvbvbdjkvn  
on line fdgdgdgdd  
on line safffasffaf  
on line adaddsd  

我怎样才能在Linux终端中做到这一点?

答案1

使用-o的选项grep仅选择所需的部分,在您的情况下,使用模式on line .*选择从on line行开始到行尾的部分:

% grep -o 'on line .*' temp.txt >new.txt

% cat new.txt 
on line jhkjhvjdbvjvbvbdjkvn  
on line fdgdgdgdd  
on line safffasffaf  
on line adaddsd

答案2

鉴于vi这个问题的标签,以及我发现自动文件编辑的事实符合 POSIX 标准的ex命令sed与、awkgrep甚至 Perl上的大量建议相比,此站点上的建议很冷淡,这里有一个 POSIX 兼容的ex命令,它将执行所需的过滤:

ex -sc 'g/.*\(on line\)/s//\1/ | .w!>>output
q!' input

注意命令中嵌入的换行符——这对于完整的 POSIX 可移植性是必要的,因为没有其他明确的方法来结束global 命令;然而最多实现允许多个-c命令,在这种情况下,以下一个衬垫将同样工作:

ex -sc 'g/.*\(on line\)/s//\1/ | .w!>>output' -c 'q!' input

ex该命令中包含一些正则表达式魔法和大量-command 魔法,并且由于ex似乎不是很广为人知,因此我将解释每个部分:

-s以静默模式启动ex,“准备批处理”,因此没有任何内容输出到您的终端。

-c意思是“打开文件时运行以下命令。” (input是要打开的文件的名称。)

ex命令本身实际上是两个命令:

g/.*\(on line\)/s//\1/ | .w!>>output
q!

g是“全局”命令,意味着“在与指定正则表达式匹配的文件的所有行上运行以下命令(该行的其余部分)”。

给出的正则表达式是.*\(on line\),这意味着“任意次数的任何字符,包括 0,后跟“on line””。括号用于捕获“在线”以便稍后反向引用。

事实上,g命令本身也可以是这样,g/on line/而且它的工作原理是一样的。但是,s我编写的替代命令使用没有什么对于它的正则表达式s//——这意味着“重用上次使用的正则表达式”。然后该s命令用于\1替换文本,在本例中表示“在线”。

|命令中的管道符号ex并不像 shell 中那样表示管道。相反,它是通常用于分隔单独的ex命令,每个命令按顺序但独立运行。然而,global 命令是一个例外:在全局命令中,竖线将所有命令分隔开。之内全局命令——即只运行此类命令在与正则表达式匹配的行上在全局命令中指定。

在这种情况下,竖线后面的命令是w仪式命令。它前面有一个点,.指定“当前行”;如果没有这个地址说明符,写命令将写入全部的文件,无论当前行是什么。 (由于我们在全局命令中使用 write 命令,因此如果我们省略点,则 write 命令将在每个匹配行已对其执行了替换命令!)

>>意味着“如果文件已经存在,则追加到它而不是给出错误”。由于我们多次写入文件,这是必要的,否则我们最终只会得到最后的写入输出文件的行。前面!>>意思是“如果文件如果已经存在,请创建该文件并写入它,而不是抛出错误。”(如果没有,!POSIX 中未指定是否会发生这种情况。)当然还有output要写入的文件的名称。

最后,当然,q!意味着“退出而不保存对当前文件的更改”。我们已经对文件的许多行进行了替换input,但我们不想保存这些更改,因此我们使用q!.


还有一些其他等效的方法,例如以下:

ex -sc '%s/.*\(on line\)/\1/e | v//d
w output | q!' input

但这使用了e替代命令的标志,而 POSIX 中没有该标志。 (如果省略此标志,则当.*\(on line\)在文件中的任何位置都找不到正则表达式时,批处理将停止。)


当然,哪里ex 真的闪耀是在到位文件编辑。但它当然可以用于将一个文件过滤到另一个文件,如上所示。

答案3

尝试这个:

grep -o 'on line .*' temp.txt > out.txt

-o参数使 grep 只输出该行的匹配部分,这就是你想要的。

答案4

如果您grep不支持该-o选项:

sed 's/^.*\(on line\)/\1/' temp.text > out.txt

或者,如果您只想要包含以下内容的行on line

sed -n 's/^.*\(on line\)/\1/p' temp.text > out.txt

请注意,如果 多次出现on line,它将打印以 开头的行部分最右边的它的发生。对于最左边的出现:

sed '/on line/!d;s//\
&/;s/.*\n//' temp.text > out.txt

相关内容