Bash 中的管道、标准输入和命令行参数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1504867/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Pipe, standard input and command line arguments in Bash
提问by Tim
Consider:
考虑:
command1 | command2
Is the output of command1 used as standard input of command2 or as command line arguments to command2?
command1 的输出是用作 command2 的标准输入还是用作 command2 的命令行参数?
For example,
例如,
cat test.sh | grep "hehe"
What is its equivalent form without using a pipe?
不使用管道的等效形式是什么?
I tried
我试过
grep "hehe" $(cat test.sh)
and it seems not to be correct.
它似乎不正确。
回答by Jonathan Leffler
grep "hehe" < test.sh
Input redirection - works for a single file only, of course, whereas cat
works for any number of input files.
输入重定向 - 当然,仅适用于单个文件,而cat
适用于任意数量的输入文件。
Consider the notations:
考虑符号:
grep "hehe" $(cat test.sh)
grep "hehe" `cat test.sh`
These are equivalent in this context; it is much easier to use the '$(cmd)
' notation in nested uses, such as:
这些在这种情况下是等效的;$(cmd)
在嵌套使用中使用 ' ' 符号要容易得多,例如:
x=$(dirname $(dirname $(which gcc)))
x=`dirname \`dirname \\`which gcc\\`\``
(This gives you the base directory in which GCC is installed, in case you are wondering.)
(这为您提供了安装 GCC 的基本目录,以防您想知道。)
In the grep
example, what happens is that the contents of test.sh
is read and split into white-space separated words, and each such word is provided as an argument to grep
. Since grep
treats the words after "hehe"
(where grep
, of course, does notsee the double quotes - and they are not needed in this case; as a general rule, use single quotes rather than double quotes, especially around complex strings like regular expressions which often use shell metacharacters)... As I was saying, grep
treats the words after "hehe"
as file names, and tries to open each file, usually failing dismally because the files do not exist. This is why the notation is not appropriate in this context.
在这个grep
例子中,发生的事情是test.sh
读取的内容并将其拆分为以空格分隔的单词,并且每个这样的单词都作为 的参数提供grep
。由于grep
处理后面的词"hehe"
( where grep
,当然,没有看到双引号 - 在这种情况下不需要它们;作为一般规则,使用单引号而不是双引号,尤其是在复杂的字符串周围,例如经常使用的正则表达式shell 元字符)...正如我所说,grep
将后面的单词"hehe"
视为文件名,并尝试打开每个文件,通常会因为文件不存在而惨淡失败。这就是该符号在这种情况下不合适的原因。
After revisiting the question, there is more that could be said - that hasn't already been said.
在重新审视这个问题之后,还有更多可以说的——还没有说。
First off, many Unix commands are designed to work as 'filters'; they read input from some files, transform it in some way, and write the result onto standard output. Such commands are designed for use within command pipelines. Examples include:
首先,许多 Unix 命令被设计为用作“过滤器”;他们从一些文件中读取输入,以某种方式对其进行转换,然后将结果写入标准输出。此类命令专为在命令管道中使用而设计。例子包括:
- cat
- grep
- troff and relatives
- awk (with caveats)
- sed
- sort
- 猫
- 格雷普
- 特洛夫和亲戚
- awk(有警告)
- sed
- 种类
All these filters have the same general behaviour: they take command line options to control their behaviour, and then they either read the files specified as command line arguments or, if there are no such arguments, they read their standard input. Some (like sort
) can have options to control where their output goes instead of standard output, but that is relatively uncommon.
所有这些过滤器都具有相同的一般行为:它们采用命令行选项来控制它们的行为,然后它们要么读取指定为命令行参数的文件,要么读取它们的标准输入,如果没有这样的参数。有些(如sort
)可以有选项来控制他们的输出去哪里而不是标准输出,但这种情况相对不常见。
There are a few pure filters - tr
is one such - that strictly read standard input and write to standard output.
有一些纯过滤器 -tr
就是这样一种 - 严格读取标准输入并写入标准输出。
Other commands have different behaviours. Eric Raymond provides a taxonomy for command types in "The Art of UNIX Programming".
其他命令有不同的行为。Eric Raymond 在“ The Art of UNIX Programming”中提供了命令类型的分类法。
Some commands generate lists of file names on standard output - the two classics are ls
and find
.
一些命令在标准输出上生成文件名列表 - 两个经典是ls
和find
。
Sometimes, you want to apply the output from a file name generator as command line arguments for a filter. There's a program that does that automatically - it is xargs
.
有时,您希望将文件名生成器的输出应用为过滤器的命令行参数。有一个程序可以自动执行此操作 - 它是xargs
.
Classically, you would use:
通常,您将使用:
find . -name '*.[chyl]' | xargs grep -n magic_name /dev/null
This would generate a complete list of files with the extensions '.c
', '.h
', '.y
' and '.l
' (C source, headers, Yacc and Lex files). As the list is read by xargs
, it would create command lines with grep -n magic_name /dev/null
at the start and each word (separated by white space) as an argument.
这将生成带有扩展名“ .c
”、“ .h
”、“ .y
”和“ .l
”的完整文件列表(C 源代码、头文件、Yacc 和 Lex 文件)。当列表被 读取时xargs
,它将创建命令行,grep -n magic_name /dev/null
以开头和每个单词(由空格分隔)作为参数。
In the old days, Unix file names didn't include spaces. Under the influence of Mac and Windows, such spaces are now common-place. The GNU versions of find
and xargs
have complementary options to deal with this problem:
在过去,Unix 文件名不包含空格。在 Mac 和 Windows 的影响下,这样的空间现在已经司空见惯。GNU 版本find
和xargs
有处理这个问题的补充选项:
find . -name '*.[chyl]' -print0 | xargs -0 grep -n magic_name /dev/null
The '-print0
' option means "print file names terminated by a NUL '\0'" (because the only characters that cannot appear in a (simple) file name are '/' and NUL, and obviously, '/' can appear in path names). The corresponding '-0
' tells xargs
to look for names terminated by NUL instead of space separated names.
' -print0
' 选项表示“打印以 NUL '\0' 结尾的文件名”(因为不能出现在(简单)文件名中的唯一字符是 '/' 和 NUL,显然,'/' 可以出现在路径中名称)。相应的“ -0
”告诉xargs
查找以 NUL 结尾的名称,而不是空格分隔的名称。
回答by Paused until further notice.
Another form of redirection is process substitution.
另一种重定向形式是进程替换。
grep "hehe" <(cat test.sh)
is equivalent to:
相当于:
grep "hehe" test.sh
which both look at the contents of test.sh
itself.
这两者都看test.sh
本身的内容。
While, as it has been noted, this command:
虽然,正如已经指出的,这个命令:
grep "hehe" $(cat test.sh)
looks for filenames in test.sh
and uses them as arguments for grep
. So if test.sh
consists of:
在 中查找文件名test.sh
并将它们用作grep
. 所以如果test.sh
包括:
scriptone
scripttwo
then grep
is going to look for "hehe" in the contents of each of those files.
然后grep
将在每个文件的内容中寻找“呵呵”。
回答by Jarett Millard
It's used as the stdin.
它用作标准输入。
Try:
尝试:
grep "hehe" - $(cat test.sh)
That might be wrong; I can't test it out on this computer. If you do it without the pipe like you tried, grep treats the last argument as a filename, ie, looks for a file called [contents of test.sh]. If you pass it a - (or don't put a last argument), you tell it to use stdin as the file.
那可能是错误的;我不能在这台电脑上测试它。如果像您尝试的那样在没有管道的情况下执行此操作,grep 会将最后一个参数视为文件名,即查找名为 [test.sh 的内容] 的文件。如果你给它传递一个 - (或者不放最后一个参数),你告诉它使用标准输入作为文件。
You can also just pass grep a file to scan through:
你也可以只通过 grep 一个文件来扫描:
grep "hehe" test.sh
...but you seem to be asking more of a generalized bash question, not really a grep usage question, so that's probably not too helpful.
...但您似乎问了更多笼统的 bash 问题,而不是真正的 grep 用法问题,所以这可能没有太大帮助。
回答by tanius
What is the equivalent of a bash pipe using command line arguments?
使用命令行参数的 bash 管道相当于什么?
Pipes and command line arguments are different forms of input that are not interchangeable. If a program allows you to have equivalent forms of both, that is the choice of that program alone. (In source code, command line arguments appear as text in a variable, while pipes appear as open files, including stdin and stdout. Bash I/O redirection syntax, as used here lateron, technically does notbelong to command line arguments, even though written right next to them on the command line …)
管道和命令行参数是不同形式的输入,不可互换。如果一个程序允许您拥有两者的等效形式,则只能选择该程序。(源代码,命令行参数显示为一个可变文本,而管显示为打开的文件,包括标准输入和标准输出。Bash的I / O重定向的语法,这里lateron使用,在技术上确实不属于命令行参数,即使在命令行上写在它们旁边……)
But let's be pedantic and also answer this:
但让我们学究,也回答这个:
What is the equivalent of a bash pipe without using a bash pipe character?
不使用 bash 管道字符的 bash 管道相当于什么?
Answer: cat test.sh | grep "hehe"
is equivalent to
答:cat test.sh | grep "hehe"
相当于
grep "hehe" < <(cat test.sh)
Explanation:
解释:
- Pipes redirect stdout of one command to stdin of another. To set the source of stdin, we can use input redirection (
< …
) instead of using the pipe character. - However, just using input redirection (
grep "hehe" < test.sh
) is not the equivalent to pipes because it uses a fileas the source for stdin, while pipes use the output a command(cat test.sh
). So in addition, we add process substitution<(…)
to replace input from a file with input from a command. Of course, the example is confusing because the two variants have the same effects:
grep "hehe" < test.sh grep "hehe" < <(cat test.sh)
But technically, input from a file is still a different mechanism than input from the output of a command that gets its input from a file.
- 管道将一个命令的标准输出重定向到另一个命令的标准输入。要设置 stdin 的来源,我们可以使用输入重定向 (
< …
) 而不是使用管道字符。 - 但是,仅使用输入重定向 (
grep "hehe" < test.sh
) 并不等同于管道,因为它使用文件作为 stdin 的源,而管道使用输出命令(cat test.sh
)。因此,此外,我们添加了进程替换<(…)
以将来自文件的输入替换为来自命令的输入。 当然,这个例子令人困惑,因为这两个变体具有相同的效果:
grep "hehe" < test.sh grep "hehe" < <(cat test.sh)
但从技术上讲,从文件输入与从从文件获取输入的命令的输出输入仍然是一种不同的机制。
Source: Advanced Bash Scripting Manual, section on process substitution(start reading at "Some other usages").
来源:高级 Bash 脚本手册,关于进程替换的部分(从“其他一些用法”开始阅读)。