Bash 管道,复制流并在每行中运行不同的命令

Bash 管道,复制流并在每行中运行不同的命令

如何在输出的第二行运行不同的命令?

例如,我有echo foo | sed p哪些输出

foo
foo

我想sed 's/foo/bar/'在其中一个上运行以获得

bar
foo

答案1

$ echo foo | sed p
foo
foo

第一个foo由命令打印p,第二个则不打印,-n因为sed在循环结束时打印模式空间(就在读取下一行之前)。

foo那么,要得到bar,那就是:

$ echo foo | sed 'p;s/foo/bar/'
foo
bar

或者:

$ echo foo | sed -n 'p;s/foo/bar/p'
foo
bar

bar仅当存在替换时才打印(通过替换成功时带有标志s的命令) 。p

要获得bar, then foo,您需要h旧的原始行并在打印替换结果后恢复它:

$ echo foo | sed 'h;s/foo/bar/p;g'
bar
foo

答案2

要运行不影响第一行数据的命令,您只需在子shell中运行它,并添加一个将读取并输出第一行的命令作为前缀,如下所示:

read -r line; echo "$line"   # pass first line unmodified
sed 's/foo/bar/'             # operate on rest of lines

所以你的整个例子看起来像这样:

$ echo foo | sed p | ( read -r line; echo "$line"; sed 's/foo/bar/')
foo
bar

答案3

最好将其传输到一个命令中,该命令可以根据输入行号执行不同的操作。

例如,与sed

$ printf 'foo\nfoo\n' | sed '2,$s/foo/bar/'
foo
bar

这使用 sed 的寻址语法,将操作限制s/foo/bar/为从 2 到文件末尾 ( 2,$) 的行。

来自man sed(GNU 版本,稍微重新格式化以使其更具可读性):

Sed 命令可以不带地址,在这种情况下,该命令将对所有输入行执行;具有一个地址,在这种情况下,仅对与该地址匹配的输入行执行该命令;或者有两个地址,在这种情况下,将对与从第一个地址开始并继续到第二个地址的行的包含范围匹配的所有输入行执行该命令。

关于地址范围需要注意的三件事:

  • 语法为addr1,addr2(即地址之间用逗号分隔);

  • 即使 addr2 选择了较早的行,addr1 匹配的行也将始终被接受;

  • 如果 addr2 是正则表达式,则不会针对 addr1 匹配的行进行测试。

或者awk

$ printf 'foo\nfoo\n' | awk 'NR > 1 {gsub(/foo/,"bar")};1'
foo
bar

或 Perl:

$ printf 'foo\nfoo\n' | perl -p -e 's/foo/bar/ if $. > 1'
foo
bar

awk 和 perl 版本测试行号(NR对于 awk,$.对于 perl)是否大于 1。

相关内容