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

时间:2009-10-01 16:04:16

标签: bash pipe

考虑:

command1 | command2

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

例如,

cat test.sh | grep "hehe"

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

我试过

grep "hehe" $(cat test.sh)

似乎不正确。

4 个答案:

答案 0 :(得分:14)

grep "hehe" < test.sh

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


考虑一下符号:

grep "hehe" $(cat test.sh)
grep "hehe" `cat test.sh`

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

x=$(dirname $(dirname $(which gcc)))
x=`dirname \`dirname \\\`which gcc\\\`\``

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

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


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

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

  • 的grep
  • troff和亲戚
  • awk(有警告)
  • SED
  • 排序

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

有一些纯粹的过滤器 - tr就是这样 - 严格读取标准输入并写入标准输出。

其他命令有不同的行为。 Eric Raymond在“The Art of UNIX Programming”中为命令类型提供了分类。

某些命令会在标准输出上生成文件名列表 - 两个经典名称为lsfind

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

经典,您可以使用:

find . -name '*.[chyl]' | xargs grep -n magic_name /dev/null

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

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

find . -name '*.[chyl]' -print0 | xargs -0 grep -n magic_name /dev/null

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

答案 1 :(得分:6)

另一种重定向形式是流程替换。

grep "hehe" <(cat test.sh)

相当于:

grep "hehe" test.sh

它们都会查看test.sh本身的内容。

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

grep "hehe" $(cat test.sh)

test.sh中查找文件名,并将其用作grep的参数。因此,如果test.sh包含:

scriptone
scripttwo

然后grep将在每个文件的内容中寻找“hehe”。

答案 2 :(得分:1)

它用作标准输入。

尝试:

grep "hehe" - $(cat test.sh)

那可能是错的;我无法在这台电脑上测试它。如果你没有像你尝试的那样使用管道,grep会将最后一个参数视为文件名,即查找名为[test.sh]的内容的文件。如果你传递它 - (或者不要放最后一个参数),你告诉它使用stdin作为文件。

您也可以传递grep文件进行扫描:

grep "hehe" test.sh

...但你似乎更多地要求一个广义的bash问题,而不是一个真正的grep使用问题,所以这可能不太有帮助。

答案 3 :(得分:0)

使用命令行参数的bash管道的等价物是什么?

管道和命令行参数是不可互换的不同输入形式。如果一个程序允许你拥有两者的等价形式,那就是单独选择那个程序。 (在源代码中,命令行参数在变量中显示为文本,而管道显示为打开文件,包括stdin和stdout .Bash I / O重定向语法,如下所用,技术上属于命令行参数,即使在命令行旁边写在它们旁边......)

但是,让我们迂腐,并回答这个问题:

不使用bash管道字符的bash管道的等价物是什么?

答案:cat test.sh | grep "hehe"相当于

grep "hehe" < <(cat test.sh)

说明:

  • 管道将一个命令的stdout重定向到另一个命令的stdin。要设置stdin的源,我们可以使用输入重定向(< …)而不是使用管道字符。
  • 但是,仅使用输入重定向(grep "hehe" < test.sh)并不等同于管道,因为它使用文件作为stdin的源,而管道使用输出命令cat test.sh)。因此,我们还添加了进程替换<(…),以使用命令输入替换文件中的输入。
  • 当然,这个例子令人困惑,因为这两个变体具有相同的效果:

    grep "hehe" < test.sh
    grep "hehe" < <(cat test.sh)
    

    但从技术上讲,来自文件的输入仍然是与从文件中获取输入的命令输出的输入不同的机制。

来源:Advanced Bash Scripting Manual, section on process substitution(开始阅读“其他一些用法”)。