大多数人都知道,shell 允许您在运行命令时重定向 stdin/stdout/stderr,还可以将输出从一个命令传输到另一个命令。
可能很少有人知道,您可以使用运算符编写有条件或无条件地依次执行的“列表”命令;
&&
||
。
这些功能如何相互作用?如果我做类似的事情
command1 && command2 >file
它是仅重定向最后一个命令的输出,还是两者都重定向?如果我写
command1 | command2 && command3 | command4
它实际上是做什么的?这是两个带有条件的管道吗?或者这是一条管道包含条件作为管道的步骤之一?
据我所知,shell 不支持添加括号来消除歧义,所以我不确定您如何请求一种解释或另一种解释......无论如何,了解 shell 如何解释会很有用所有这些。 (像大多数人一样,我只使用 Bash。)
答案1
在命令中
command1 && command2 >file
的输出command1
未重定向,但 的输出command2
是:
$ echo hello && echo ok >file
hello
$ cat file
ok
重定向command1
可以单独完成:
command1 >file1 && command2 >file2
在命令中
command1 | command2 && command3 | command4
的输出command1
通过管道传输到command2
.如果第一个管道以零退出状态终止,则第二个管道将以类似的方式运行:
$ echo hello | cat && echo bye | cat
hello
bye
如果command2 && command3
要对列表进行分组,则将其写为
command1 | { command2 && command3; } | command4
这意味着 的输出通过command1
管道传送到复合命令command2 && command3
。然后复合命令的输出通过管道传输到command4
:
$ echo hello | { read message && printf 'We got "%s"\n' "$message"; } | rev
"olleh" tog eW
单个简单命令(见下文)可以被重定向:
$ echo hello | { read message && printf 'We got "%s"\n' "$message"; echo bye >&2; } | rev
bye
"olleh" tog eW
&&
在 shell 语法中,“完整命令”由一系列由或分隔的管道组成||
。这是非常宽松请讲。这意味着&&
和在管道中的||
优先级高于。|
另一方面,重定向与当前命令紧密结合,因为语法使重定向成为“简单命令”构造的一部分。一个简单的命令是一些命令前缀、命令名称和命令后缀(其中前缀和后缀是可选的)。命令前缀可以是对环境变量的赋值 ( VAR=value myscript
),也可以是重定向 ( >outfile cat
)。同样,命令后缀可以是重定向 ( cat >outfile
) 等。
显然,“复合命令”也可以重定向。复合命令是{ ...; }
大括号组中或子 shell 中的管道(可能是单个简单命令) ,( ... )
或者是if
、while
、for
、until
、 或case
语句。
POSIX shell(其bash
扩展)的完整语法可在 POSIX 标准中找到。以下只是语法规则的顶层:
program : linebreak complete_commands linebreak
| linebreak
;
complete_commands: complete_commands newline_list complete_command
| complete_command
;
complete_command : list separator_op
| list
;
list : list separator_op and_or
| and_or
;
and_or : pipeline
| and_or AND_IF linebreak pipeline
| and_or OR_IF linebreak pipeline
;
pipeline : pipe_sequence
| Bang pipe_sequence
;
pipe_sequence : command
| pipe_sequence '|' linebreak command
(参考:https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_10_02)