假设我通过管道输出命令的输出并希望使用 grep 过滤行,但还要保留第一个作为标题的行。我看到有人键入类似这样的内容:
the command | (read l; echo$l) | grep bla | less
它提取了第一行(标题),然后将文件的其余部分用于匹配 bla 的行,并将其输出less用于检查。当然上面的命令不起作用,但这就是想法,它的哪一部分是错误的?
假设我通过管道输出命令的输出并希望使用 grep 过滤行,但还要保留第一个作为标题的行。我看到有人键入类似这样的内容:
the command | (read l; echo$l) | grep bla | less
它提取了第一行(标题),然后将文件的其余部分用于匹配 bla 的行,并将其输出less用于检查。当然上面的命令不起作用,但这就是想法,它的哪一部分是错误的?
使用 awk:
command | awk 'NR==1||/bla/'
感谢@doubleDown 指出这{print}是不必要的,因为它是默认操作。
使用 perl:
command | perl -ne 'print if $.==1 or /bla/'
(如果您需要 perl 不规则表达式,perl 可能可用 :) )
sed味道:
command | sed -ne '1p' -ne '/bla/p'
grep第一行之外的所有内容以下内容大部分会得到你想要的,但它有几个缺陷(见本文末尾):
the command | (read l; echo $l; grep blah) | less
相反,我建议创建和使用以下函数:
grep1 () (
IFS= read -r line
printf %s\\n "${line}"
grep "$@"
)
以下是您将如何使用它:
the command | grep1 blah | less
实际操作示例:
$ ps -ef | grep1 firefox
UID PID PPID C STIME TTY TIME CMD
rhansen 3654 3311 4 13:33 ? 00:07:59 /usr/lib/firefox/firefox
read使用第一行输入command并将其(未修改)分配给变量lineprintf输出line(未修改)的值grep第一行永远不会通过grep,因此没有机会将其过滤掉。
( ... )而不是{ ... }因为我不希望函数体中的变量分配影响调用者的环境(括号导致它在子shell中运行,从而将任何更改与调用者隔离开来)。IFS=防止read剥离前导和尾随空格-r参数read(第一行完全保留在变量中line)printf %s\\n而不是echo因为echo可能会处理反斜杠,可能导致输出的第一行与原始的第一行不同上面的函数有一个小问题:如果给定空输入,它将打印一个空行。以下内容避免了该问题:
grep1_better() (
IFS= read -r line && printf %s\\n "${line}"
grep "$@"
)
这是有效的,因为read如果遇到输入结束,则返回非零返回码。如果没有输入,read将“失败”(返回非零)并且&&将跳过printf.
但是,现在有一个新问题:如果有输入,但根本没有任何换行符(例如,printf %s foo),该函数将不输出任何内容。这是因为read即使有一些输入,也会遇到输入结束和“失败”。以下是可以解决的方法:
grep1_even_better() (
IFS= read -r line || [ -n "${line}" ] && printf %s\\n "${line}"
grep "$@"
)
在英语中,上面说:“读取一行输入。如果没有遇到输入的结尾,或者如果读取了某些内容,则打印读取的内容。然后运行grep。”
进一步的改进是检测何时使用一个或多个文件名参数调用函数并做出相应的反应(从文件而不是标准输入读取)。
以下代码不起作用:
the command | (read l; echo $l) | grep bla | less
有两个主要问题:
grep,因此grep仍然可以将其过滤掉。the command”命令永远不会有机会输出剩余的行(模缓冲),因为第二阶段没有人在等待读取它们。)此外,还有一些小问题:
IFS在调用之前没有设置为空字符串read,read所以在分配变量之前会去除第一行的前导和尾随空格l。-r未传递给read,所以read将尝试解释第一输入行中的反斜杠。这可能会破坏第一行。echo没有用双引号括起来,所以制表符和多个连续的空格将被转换为单个空格。如果第一行包含列标题,这将破坏与以下行的对齐。echo可能会在其参数中处理反斜杠,所以第一行可能已损坏。-,echo可能会将字符串解释为选项,而不是要打印的内容。这些小问题也存在于 中the command | (read l; echo $l; grep blah) | less,这就是我推荐该grep1()功能的原因。
上面的awk和sed答案通常是要走的路。有时,当所讨论的正则表达式很复杂并且grep是唯一选项时,以下tee基于 - 的选项应该可以工作。这里tee将其输入写入两个“文件”,grep并head通过进程替换使用输入。tee还将其输入写入标准输出,在这种情况下需要重定向到/dev/null. sleep需要确保在head之前返回其输出grep
command | tee >(sleep 1; grep regex) >(head -1) >/dev/null