大多数人都知道,shell 允许您在运行命令时重定向 stdin/stdout/stderr,还可以将输出从一个命令传送到另一个命令。
可能很少有人知道,您可以编写“列表”命令,使用; && ||运算符有条件或无条件地一个接一个执行。
这些功能如何相互作用?如果我做类似的事情
command1 && command2 >file
Run Code Online (Sandbox Code Playgroud)
它是只重定向最后一个命令的输出,还是两者都重定向?如果我写
command1 | command2 && command3 | command4
Run Code Online (Sandbox Code Playgroud)
它实际上是做什么的?这两条管道是有条件的吗?或者这是一个包含条件作为管道步骤之一的管道?
据我所知,shell 不支持添加括号来消除歧义,所以我不确定您如何请求一种解释或另一种解释......无论如何,了解 shell 如何解释会很有用这些所有。(像大多数人一样,我只使用 Bash。)
在命令中
command1 && command2 >file
Run Code Online (Sandbox Code Playgroud)
的输出command1没有重定向,但输出command2是:
$ echo hello && echo ok >file
hello
$ cat file
ok
Run Code Online (Sandbox Code Playgroud)
重定向command1可以单独完成:
command1 >file1 && command2 >file2
Run Code Online (Sandbox Code Playgroud)
在命令中
command1 | command2 && command3 | command4
Run Code Online (Sandbox Code Playgroud)
的输出通过command1管道传输到command2. 如果第一个管道以零退出状态终止,则第二个管道以类似的方式运行:
$ echo hello | cat && echo bye | cat
hello
bye
Run Code Online (Sandbox Code Playgroud)
如果要对command2 && command3列表进行分组,则将其写为
command1 | { command2 && command3; } | command4
Run Code Online (Sandbox Code Playgroud)
这意味着 的输出通过command1管道传输到复合命令command2 && command3。复合命令的输出然后通过管道传输到command4:
$ echo hello | { read message && printf 'We got "%s"\n' "$message"; } | rev
"olleh" tog eW
Run Code Online (Sandbox Code Playgroud)
可以重定向单个简单命令(见下文):
$ echo hello | { read message && printf 'We got "%s"\n' "$message"; echo bye >&2; } | rev
bye
"olleh" tog eW
Run Code Online (Sandbox Code Playgroud)
在 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
Run Code Online (Sandbox Code Playgroud)
(参考:https : //pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_10_02)