Bash中的管道,标准输入和命令行参数

Tim*_*Tim 8 bash pipe

考虑:

command1 | command2
Run Code Online (Sandbox Code Playgroud)

command1的输出是用作command2的标准输入还是用作command2的命令行参数?

例如,

cat test.sh | grep "hehe"
Run Code Online (Sandbox Code Playgroud)

没有使用管道的等效形式是什么?

我试过了

grep "hehe" $(cat test.sh)
Run Code Online (Sandbox Code Playgroud)

而且似乎不正确.

Jon*_*ler 14

grep "hehe" < test.sh
Run Code Online (Sandbox Code Playgroud)

输入重定向 - 当然仅适用于单个文件,而cat适用于任意数量的输入文件.


考虑一下这些符号:

grep "hehe" $(cat test.sh)
grep "hehe" `cat test.sh`
Run Code Online (Sandbox Code Playgroud)

在这方面,这些是相同的; $(cmd)在嵌套用法中使用' '表示法要容易得多,例如:

x=$(dirname $(dirname $(which gcc)))
x=`dirname \`dirname \\\`which gcc\\\`\``
Run Code Online (Sandbox Code Playgroud)

(这会为您提供安装GCC的基本目录,以防您想知道.)

在该grep示例中,所发生的test.sh是读取内容并将其拆分为空格分隔的单词,并且每个这样的单词作为参数提供grep.由于grep后话治疗"hehe"(其中grep,当然,也没有看到双引号-他们不是在这种情况下需要;作为一般规则,使用单引号,而不是双引号,尤其是在复杂的字符串像正则表达式经常使用shell metacharacters)...正如我所说的那样,grep将文字名后面的单词"hehe"视为文件名,并尝试打开每个文件,因为文件不存在,通常会失败.这就是为什么符号在这种情况下不合适的原因.


在重新审视这个问题后,还有更多可以说的内容 - 尚未说过.

首先,许多Unix命令被设计为"过滤器"; 他们从一些文件中读取输入,以某种方式对其进行转换,并将结果写入标准输出.此类命令旨在用于命令管道.例子包括:

  • grep的
  • 特洛夫和亲戚
  • awk(有警告)
  • SED
  • 分类

所有这些过滤器都具有相同的一般行为:它们使用命令行选项来控制它们的行为,然后它们读取指定为命令行参数的文件,或者,如果没有这样的参数,它们读取它们的标准输入.有些(比如sort)可以选择控制输出的位置而不是标准输出,但这种情况相对不常见.

有一些纯滤波器 - tr就是这样 - 严格读取标准输入并写入标准输出.

其他命令有不同的行为.Eric Raymond在" UNIX编程的艺术 "中为命令类型提供了分类.

一些命令在标准输出上生成文件名列表 - 两个经典是lsfind.

有时,您希望将文件名生成器的输出应用为过滤器的命令行参数.有一个程序自动执行 - 它是xargs.

传统上,你会使用:

find . -name '*.[chyl]' | xargs grep -n magic_name /dev/null
Run Code Online (Sandbox Code Playgroud)

这将生成一个包含扩展名' .c',' .h',' .y'和' .l'(C源代码,标题,Yacc和Lex文件)的完整文件列表.当列表被读取时xargs,它将grep -n magic_name /dev/null在开头创建命令行,并且每个单词(由空格分隔)作为参数.

在过去,Unix文件名不包含空格.在Mac和Windows的影响下,这样的空间现在已经很普遍了.GNU版本findxargs有补充选项来处理这个问题:

find . -name '*.[chyl]' -print0 | xargs -0 grep -n magic_name /dev/null
Run Code Online (Sandbox Code Playgroud)

' -print0'选项意味着"打印文件名由NUL'\ 0'终止"(因为唯一不能出现在(简单)文件名中的字符是'/'和NUL,显然,'/'可以出现在路径中名).相应的' -0'告诉xargs查找由NUL终止的名称而不是空格分隔的名称.

  • 我不确定你是否回答了原来的问题.答案是"管道连接stdout到stdin." (3认同)

Pau*_*ce. 6

另一种重定向形式是进程替换.

grep "hehe" <(cat test.sh)
Run Code Online (Sandbox Code Playgroud)

相当于:

grep "hehe" test.sh
Run Code Online (Sandbox Code Playgroud)

它们都看着test.sh自己的内容.

虽然,正如已经指出的那样,这个命令:

grep "hehe" $(cat test.sh)
Run Code Online (Sandbox Code Playgroud)

查找文件名test.sh并将其用作参数grep.所以,如果test.sh包括:

scriptone
scripttwo
Run Code Online (Sandbox Code Playgroud)

然后grep将在每个文件的内容中寻找"hehe".

  • 我不喜欢你在第3行使用'等效':`grep"hehe"test.sh`最终使用test.sh的内容作为stdin,而`grep"hehe"<(cat test.sh )``使用命令`cat test.sh`的输出作为stdin.如果你按如下方式运行命令,请考虑结果的差异:`grep -H"hehe"<(cat test.sh)`和`grep -H"hehe"test.sh` (2认同)