ter*_*don 353 shell io-redirection control-flow
我经常在网上看到用不同符号连接各种命令的教程。例如:
command1 | command2
command1 & command2
command1 || command2
command1 && command2
Run Code Online (Sandbox Code Playgroud)
其他人似乎将命令连接到文件:
command1 > file1
command1 >> file1
Run Code Online (Sandbox Code Playgroud)
这些是什么?他们叫什么?他们在做什么?还有更多吗?
ter*_*don 490
这些被称为 shell 操作符,是的,还有更多。我将简要概述两个主要类中最常见的类,控制运算符和重定向运算符,以及它们在 bash shell 中的工作方式。
在 shell 命令语言中,执行控制功能的标记。
它是以下符号之一:Run Code Online (Sandbox Code Playgroud)& && ( ) ; ;; <newline> | ||
而|&
在bash。
一个!
是没有控制操作员,但一个保留字。它成为算术表达式和测试结构内部的逻辑 NOT [否定运算符] (同时仍需要空格分隔符)。
;
: 无论第一个命令的结果如何,都会在另一个命令完成后运行一个命令。
command1 ; command2
Run Code Online (Sandbox Code Playgroud)
首先command1
是在前台运行,一旦完成,command2
将运行。
不在字符串文字中或在某些关键字之后的换行符不等同于分号运算符。带;
分隔符的简单命令列表仍然是一个列表——因为在 shell 的解析器中,;
在执行之前仍然必须继续读入跟随带分隔符的简单命令的简单命令,而换行符可以分隔整个命令列表——或列表列表。区别很微妙,但很复杂:鉴于 shell 之前没有读取换行符后的数据的命令,换行符标记了 shell 可以开始评估它已经读入的简单命令的点,而;
分号则可以不是。
&
:这将在后台运行一个命令,允许您继续在同一个 shell 中工作。
command1 & command2
Run Code Online (Sandbox Code Playgroud)
这里,command1
在后台command2
启动并立即在前台运行,无需等待command1
退出。
后面的换行符command1
是可选的。
&&
: 用于构建 AND 列表,它允许您仅在另一个命令成功退出时运行一个命令。
command1 && command2
Run Code Online (Sandbox Code Playgroud)
在这里,command2
将运行后command1
已经完成,只当command1
是成功的(如果它的退出代码为0)。这两个命令都在前台运行。
这个命令也可以写
if command1
then command2
else false
fi
Run Code Online (Sandbox Code Playgroud)
或者只是if command1; then command2; fi
如果忽略返回状态。
||
: 用于构建 OR 列表,它允许您仅在另一个命令退出失败时运行一个命令。
command1 || command2
Run Code Online (Sandbox Code Playgroud)
在这里,command2
只有在command1
失败时才会运行(如果它返回非 0 的退出状态)。这两个命令都在前台运行。
这个命令也可以写
if command1
then true
else command2
fi
Run Code Online (Sandbox Code Playgroud)
或以更短的方式if ! command1; then command2; fi
。
注意&&
和||
是左结合的;参见shell 逻辑运算符 &&、|| 的优先级 想要查询更多的信息。
!
: 这是一个保留字,用作“非”操作符(但必须有一个分隔符),用于否定命令的返回状态 - 如果命令返回非零状态,则返回 0,如果返回状态 0,则返回 1 . 也是该test
实用程序的逻辑 NOT 。
! command1
[ ! a = a ]
Run Code Online (Sandbox Code Playgroud)
算术表达式中的真正 NOT 运算符:
$ echo $((!0)) $((!23))
1 0
Run Code Online (Sandbox Code Playgroud)|
: 管道运算符,它将一个命令的输出作为输入传递给另一个。从管道操作符构建的命令称为管道。
command1 | command2
Run Code Online (Sandbox Code Playgroud)
打印的任何输出command1
都作为输入传递给command2
.
|&
: 这是2>&1 |
bash 和 zsh的简写。它将一个命令的标准输出和标准错误作为输入传递给另一个命令。
command1 |& command2
Run Code Online (Sandbox Code Playgroud);;
仅用于标记case 语句的结束。Ksh、bash 和 zsh 还支持;&
进入下一个案例和;;&
(不在 ATT ksh 中)继续并测试后续案例。
(
并)
用于对命令进行分组并在子外壳中启动它们。{
并对}
命令进行分组,但不要在子 shell 中启动它们。有关shell 语法中各种类型的括号、方括号和大括号的讨论,请参阅此答案。
在 shell 命令语言中,执行重定向功能的标记。它是以下符号之一:
Run Code Online (Sandbox Code Playgroud)< > >| << >> <& >& <<- <>
这些允许您控制命令的输入和输出。它们可以出现在简单命令中的任何位置,也可以跟随命令。重定向按照它们出现的顺序进行处理,从左到右。
<
: 为命令提供输入。
command < file.txt
Run Code Online (Sandbox Code Playgroud)
以上将command
在file.txt
.
<>
: 同上,但文件以读+写模式打开,而不是只读模式:
command <> file.txt
Run Code Online (Sandbox Code Playgroud)
如果文件不存在,它将被创建。
该运算符很少使用,因为命令通常只从其标准输入中读取,尽管它在许多特定情况下可以派上用场。
>
: 将命令的输出定向到文件中。
command > out.txt
Run Code Online (Sandbox Code Playgroud)
以上将把输出保存command
为out.txt
. 如果文件存在,它的内容将被覆盖,如果它不存在,它将被创建。
此运算符也常用于选择是否应将某些内容打印到标准错误或标准输出:
command >out.txt 2>error.txt
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,>
将重定向标准输出并2>
重定向标准错误。输出也可以使用1>
但是重定向,因为这是默认值,1
通常会省略 并且它简单地写为>
.
因此,运行command
在file.txt
并保存其输出out.txt
和任何错误消息在error.txt
你可以运行:
command < file.txt > out.txt 2> error.txt
Run Code Online (Sandbox Code Playgroud)>|
: 与 相同>
,但会覆盖目标,即使外壳已配置为拒绝覆盖(使用set -C
或set -o noclobber
)。
command >| out.txt
Run Code Online (Sandbox Code Playgroud)
如果out.txt
存在,则输出command
将替换其内容。如果它不存在,它将被创建。
>>
: 与 相同>
,只是如果目标文件存在,则附加新数据。
command >> out.txt
Run Code Online (Sandbox Code Playgroud)
如果out.txt
存在, 的输出command
将附加到它之后,在它已经存在的任何东西之后。如果它不存在,它将被创建。
>&
:(根据 POSIX 规范)当被数字( 1>&2
)包围或-
在右侧 ( 1>&-
) 时,要么仅重定向一个文件描述符,要么将其关闭 ( >&-
)。
A>&
后跟文件描述符编号是重定向文件描述符>&-
的可移植方式,也是关闭文件描述符的可移植方式。
如果此重定向的右侧是一个文件,请阅读下一个条目。
>&
, &>
,>>&
和&>>
:(也请阅读上面的内容)分别重定向标准错误和标准输出,替换或附加。
command &> out.txt
Run Code Online (Sandbox Code Playgroud)
的标准错误和标准输出都command
将保存在 中out.txt
,如果它不存在,则覆盖其内容或创建它。
command &>> out.txt
Run Code Online (Sandbox Code Playgroud)
同上,除了如果out.txt
存在,command
将附加输出和错误。
该&>
变种起源于bash
,而>&
变异来自(几十年前)CSH。它们都与其他 POSIX shell 操作符冲突,不应在可移植sh
脚本中使用。
<<
: 一个here文件。它通常用于打印多行字符串。
command << WORD
Text
WORD
Run Code Online (Sandbox Code Playgroud)
在这里,command
将获取所有内容,直到找到下一个出现的WORD
,Text
在上例中,作为输入。虽然WORD
通常是EoF
或其变体,但它可以是您喜欢的任何字母数字(不仅是)字符串。当WORD
被引用,在这里文档中的文本字面上处理,(例如在变量)没有进行扩展。如果不加引号,变量将被扩展。有关更多详细信息,请参阅bash 手册。
如果要将 的输出command << WORD ... WORD
直接通过管道传输到另一个或多个命令中,则必须将管道与 放在同一行<< WORD
,不能将其放在终止 WORD 之后或下一行。例如:
command << WORD | command2 | command3...
Text
WORD
Run Code Online (Sandbox Code Playgroud)<<<
: here 字符串,类似于 here 文档,但用于单行。这些仅存在于 Unix 端口或 rc(它的起源地)、zsh、ksh、yash 和 bash 的一些实现中。
command <<< WORD
Run Code Online (Sandbox Code Playgroud)
给出的任何内容都WORD
被扩展,其值作为输入传递给command
。这通常用于将变量的内容作为输入传递给命令。例如:
$ foo="bar"
$ sed 's/a/A/' <<< "$foo"
bAr
# as a short-cut for the standard:
$ printf '%s\n' "$foo" | sed 's/a/A/'
bAr
# or
sed 's/a/A/' << EOF
$foo
EOF
Run Code Online (Sandbox Code Playgroud)其他一些运算符 ( >&-
, x>&y
x<&y
) 可用于关闭或复制文件描述符。有关它们的详细信息,请参阅您的shell的手册中的相关部分(这里例如对于bash)。
这仅涵盖了 Bourne-like shell 的最常见运算符。一些 shell 有一些额外的重定向操作符。
Ksh、bash 和 zsh 也有构造<(…)
, >(…)
and =(…)
(zsh
仅后一个)。这些不是重定向,而是进程替换。
Sco*_*ott 74
刚刚了解 I/O 重定向(<
和>
)的Unix 初学者经常尝试像
命令... input_file > the_same_file
或者
命令... <文件 > the_same_file
或者,几乎等效地,
猫文件| 命令……> the_same_file
(grep
、sed
、cut
、sort
和spell
是人们倾向于在此类结构中使用的命令示例。)用户惊讶地发现这些方案会导致文件变空。
在bash(1)的重定向部分的第一句中,可以发现其他答案中似乎没有提到的细微差别:
在执行命令之前,可以 使用由 shell 解释的特殊符号重定向其输入和输出。
前五个字应为粗体、斜体、下划线、放大、闪烁、红色,并标有 图标,强调 shell 在执行命令之前执行请求的重定向这一事实
。还要记住
输出重定向导致文件……被打开以进行写入……。如果文件不存在,则创建它;如果确实存在,则将其截断为零大小。
所以,在这个例子中:
command … input_file > the_same_file
在程序开始运行roster
之前,外壳程序打开文件进行写入、截断它(即丢弃其所有内容)sort
。自然,无法采取任何措施来恢复数据。
人们可能会天真地期望
command … < file > the_same_file
可能会更好。因为 shell 处理从左到右的重定向,所以在打开它进行写入(对于标准输出)之前,它会打开poem
以进行读取(对于tr
的标准输入)。但它没有帮助。尽管这一系列操作产生了两个文件句柄,但它们都指向同一个文件。当 shell 打开文件进行读取时,内容仍然存在,但在程序执行之前它们仍然会被破坏。
解决方案包括:
检查您正在运行的程序是否有自己的内部功能来指定输出的去向。这通常由-o
(或--output=
)标记表示。特别是,
cat file | command … > the_same_file
大致相当于
sort roster > roster
Run Code Online (Sandbox Code Playgroud)
除了在第一种情况下,sort
程序打开输出文件。并且在它读取所有输入文件之前不打开输出文件是足够聪明的。
同样地,至少某些版本sed
有一个-i
(编辑我N将),可以用来写输出回给输入文件(再次,选择后所有的输入都被读取)。诸如ed
/ ex
、emacs
、pico
和vi
/ 之类的编辑器vim
允许用户编辑文本文件并将编辑后的文本保存在原始文件中。请注意,ed
(至少)可以以非交互方式使用。
vi
有一个相关的功能。如果您键入,它会将编辑缓冲区的内容写出到,读取输出,并将其插入缓冲区(替换原始内容)。:%!command
Entercommand
简单但有效:
命令... input_file > temp_file && mv temp_file input_file
这有一个缺点,如果input_file
是一个链接,它将(可能)被一个单独的文件替换。此外,新文件将由您拥有,并具有默认保护。特别是,这会带来文件最终成为世界可读的风险,即使原始文件input_file
不是。
变化:
command … input_file > temp_file && cp temp_file input_file && rm temp_file
temp_file
世界可读。更好的是:cp input_file temp_file && command … temp_file > input_file && rm temp_file
-a
或-p
on选项cp
来告诉它保留属性。)command … input_file > temp_file &&
cp --attributes-only --preserve=all input_file temp_file &&
mv temp_file input_file
这个博客 (文件的“就地”编辑)建议和解释
{ rm input_file && 命令……> input_file ; } < INPUT_FILE
这要求command
能够处理标准输入(但几乎所有过滤器都可以)。博客本身称这是一种危险的混搭,并劝阻其使用。这还将创建一个新的、单独的文件(未链接到任何内容),由您拥有并具有默认权限。
moreutils 包有一个命令sponge
:
命令... input_file | 海绵the_same_file
有关更多信息,请参阅此答案。
这是让我完全惊讶的事情: syntaxerror 说:
[这些解决方案中的大多数] 将在只读文件系统上失败,其中“只读”意味着您
$HOME
将是可写的,但/tmp
将是只读的(默认情况下)。例如,如果您有 Ubuntu,并且您已经启动到故障恢复控制台,则通常是这种情况。此外,在这里,单证操作员<<<
将无法正常工作有两种,因为它需要/tmp
进行读/写 ,因为它会写一个临时文件到那里。
(参见这个问题包括一个strace
'd 输出)
在这种情况下,以下可能有效:
sort
,或者tr
没有的-d
或-s
选项),你可以试试
命令... input_file | dd of= the_same_file conv= notrunc请参阅此答案 和此答案以获取更多信息,包括对上述内容的解释,以及如果您的命令保证产生与输入相同或更少的输出数据量(例如,
grep
, 或cut
),则可以使用的替代方法。这些答案的优点是它们不需要任何可用空间(或者它们只需要很少的空间)。上面表格的答案
明确要求系统有足够的可用空间,以便能够同时保存整个输入(旧)文件和输出(新)文件;对于大多数其他解决方案(例如和)来说,这显然不是正确的。例外:可能需要大量可用空间,因为command … input_file > temp_file && …
sed -i
sponge
sort … | dd …
sort
在写入任何输出之前需要读取其所有输入,并且它可能会在临时文件中缓冲大部分数据(如果不是全部)。命令... input_file 1<> the_same_file可能相当于
dd
上面的答案。在语法上打开文件描述符命名文件的输入和输出,但不截断它-排序的组合
和。注意:在这种情况下,某些程序(例如和)可能会拒绝运行,因为它们可以检测到输入和输出是同一个文件。请参阅此答案
以了解上述讨论,以及如果您的命令保证产生与输入相同数量的输出数据或更少,则使此答案起作用的脚本。
警告:我没有测试过 Peter 的脚本,所以我不保证它。n<> file
n
n<
n>
cat
grep
这是 U&L 上的热门话题;它在以下问题中得到解决:
iconv
用转换后的输出替换输入文件?shuf file > file
留下一个空文件?sort
命令给我一个空文件?tr
标准输出重定向到文件……这还不包括超级用户或 Ask Ubuntu。我在此答案中纳入了上述问题的答案中的大量信息,但并非全部。(即,有关更多信息,请阅读上面列出的问题及其答案。)
PS 我与上面引用的博客没有任何关系。
G-M*_*ca' 33
;
,&
,(
和)
请注意,terdon 的答案中的某些命令可能为空。例如,你可以说
command1 ;
Run Code Online (Sandbox Code Playgroud)
(没有command2
)。这相当于
command1
Run Code Online (Sandbox Code Playgroud)
(即,它只是command1
在前台运行并等待它完成。相比之下,
command1 &
Run Code Online (Sandbox Code Playgroud)
( 没有command2
) 将command1
在后台启动,然后立即发出另一个 shell 提示。
相比之下,command1 &&
、command1 ||
和command1 |
没有任何意义。如果您键入其中一个,shell 将(可能)假定该命令继续到另一行。它将显示辅助(继续)shell 提示,通常设置为>
,并继续阅读。在 shell 脚本中,它只会读取下一行并将其附加到它已经读取的内容中。(注意:这可能不是您想要发生的。)
注意:某些 shell 的某些版本可能会将此类不完整的命令视为错误。在这种情况下(或者,实际上,在任何情况下,您有一个长命令),您可以\
在行尾放置一个反斜杠 ( ) 来告诉 shell 继续读取另一行的命令:
command1 && \
command2
Run Code Online (Sandbox Code Playgroud)
或者
find starting-directory -mindepth 3 -maxdepth 5 -iname "*.some_extension" -type f \
-newer some_existing_file -user fred -readable -print
Run Code Online (Sandbox Code Playgroud)正如terdon说,(
和)
可用于组命令。他们与该讨论“并不真正相关”的说法值得商榷。terdon 回答中的一些命令可能是 command groups。例如,
( command1 ; command2 ) && ( command3; command4 )
Run Code Online (Sandbox Code Playgroud)
做这个:
command1
并等待它完成。command2
并等待它完成。那么,如果command2
成功,
command3
并等待它完成。command4
并等待它完成。如果command2
失败,停止处理命令行。
在括号外,|
绑定得非常紧密,所以
command1 | command2 || command3
Run Code Online (Sandbox Code Playgroud)
相当于
( command1 | command2 ) || command3
Run Code Online (Sandbox Code Playgroud)
和&&
并||
结合除更紧;
,因此
command1 && command2 ; command3
Run Code Online (Sandbox Code Playgroud)
相当于
( command1 && command2 ) ; command3
Run Code Online (Sandbox Code Playgroud)
即,command3
无论command1
和/或的退出状态如何,都将被执行command2
。
归档时间: |
|
查看次数: |
166949 次 |
最近记录: |