我经常在网上看到教程,这些教程将各种命令与不同的符号相连。例如:
command1 | command2
command1 & command2
command1 || command2
command1 && command2
其他人似乎正在将命令连接到文件:
command1 > file1
command1 >> file1
这些是什么东西?他们叫什么?他们在做什么?还有更多吗?
我经常在网上看到教程,这些教程将各种命令与不同的符号相连。例如:
command1 | command2
command1 & command2
command1 || command2
command1 && command2
其他人似乎正在将命令连接到文件:
command1 > file1
command1 >> file1
这些是什么东西?他们叫什么?他们在做什么?还有更多吗?
Answers:
这些被称为shell运算符,是的,它们更多。我将简要概述一下两个主要类(控制操作符和重定向操作符)中最常见的类,以及它们相对于bash shell的工作方式。
在shell命令语言中,是执行控制功能的令牌。
它是以下符号之一:& && ( ) ; ;; <newline> | ||
并|&
以重击。
一个!
是没有控制操作员,但一个保留字。它成为算术表达式内部和测试构造内部的逻辑NOT [求反运算符] (尽管仍然需要空格分隔符)。
;
:不管另一个命令的结果如何,都将在另一个命令完成后再运行一个命令。
command1 ; command2
首先command1
运行,在前台运行,一旦完成,command2
将运行。
不在字符串文字中或某些关键字之后的换行符不等于分号运算符。列表;
分隔简单的命令仍然是一个清单 -在shell的语法分析器仍旧必须继续遵循一个简单的命令来读取;
执行前界定简单的命令,而新行可以界定一个完整的命令列表-列表或列表。区别是微妙的,但很复杂:鉴于外壳程序没有在换行符之后读取数据的强制性,换行符标志着外壳程序可以开始评估已经读取的简单命令的点,而;
分号可以不。
&
:这将在后台运行命令,使您可以继续在同一Shell中工作。
command1 & command2
在这里,command1
在后台command2
启动,并立即在前台运行,而无需等待command1
退出。
之后的换行符command1
是可选的。
&&
:用于构建AND列表,它使您仅在另一个命令成功退出时才运行一个命令。
command1 && command2
在这里,command2
将运行后command1
已经完成,只当command1
是成功的(如果它的退出代码为0)。这两个命令都在前台运行。
这个命令也可以写成
if command1
then command2
else false
fi
或者只是简单地if command1; then command2; fi
忽略返回状态。
||
:用于构建OR列表,它仅在另一个命令退出失败时才允许您运行一个命令。
command1 || command2
在这里,command2
仅在command1
失败时运行(如果返回的退出状态不是0)。这两个命令都在前台运行。
这个命令也可以写成
if command1
then true
else command2
fi
或用较短的方式if ! command1; then command2; fi
。
注意,&&
和||
是左关联的;请参阅外壳程序逻辑运算符&&,||的优先级。欲获得更多信息。
!
:这是一个保留字,用作“非”运算符(但必须具有定界符),用于取消命令的返回状态-如果命令返回非零状态,则返回0;如果返回状态0,则返回1 。对于该test
实用程序也是一个逻辑非。
! command1
[ ! a = a ]
以及算术表达式中真正的NOT运算符:
$ echo $((!0)) $((!23))
1 0
|
:管道运算符,它将一个命令的输出作为输入传递给另一个。由管道运算符生成的命令称为管道。
command1 | command2
所打印的任何输出command1
都将作为输入传递给command2
。
|&
:这是2>&1 |
bash和zsh 的简写。它会将一个命令的标准输出和标准错误传递给另一个命令。
command1 |& command2
;;
仅用于标记案例陈述的结尾。Ksh,bash和zsh还支持;&
进入下一个案例,并且;;&
(在ATT ksh中不支持)继续测试后续案例。
(
并)
用于对命令进行分组并在子shell中启动它们。{
以及对}
命令进行分组,但不要在子shell中启动它们。请参阅此答案以获取有关Shell语法中各种类型的括号,方括号和大括号的讨论。
在Shell命令语言中,是执行重定向功能的令牌。它是以下符号之一:
< > >| << >> <& >& <<- <>
这些使您可以控制命令的输入和输出。它们可以出现在简单命令内的任何位置,也可以跟随命令。重定向按照从左到右的顺序显示。
<
:输入命令。
command < file.txt
以上将对command
的内容执行file.txt
。
<>
:与上述相同,但是文件以读写模式打开,而不是只读模式:
command <> file.txt
如果文件不存在,将创建它。
很少使用该运算符,因为命令通常仅从其stdin中读取,尽管在某些特定情况下它可能会派上用场。
>
:将命令的输出定向到文件中。
command > out.txt
上面将保存输出command
为out.txt
。如果文件存在,则其内容将被覆盖;如果文件不存在,则将创建它。
此运算符还经常用于选择是将某些内容打印到标准错误还是标准输出中:
command >out.txt 2>error.txt
在上面的示例中,>
将重定向标准输出并2>
重定向标准错误。也可以使用重定向输出,1>
但是由于这是默认设置,因此1
通常会省略并将其简单地编写为>
。
因此,运行command
在file.txt
并保存其输出out.txt
和任何错误消息在error.txt
你可以运行:
command < file.txt > out.txt 2> error.txt
>|
:与相同>
,但是即使外壳已配置为拒绝覆盖(使用set -C
或set -o noclobber
),也将覆盖目标。
command >| out.txt
如果out.txt
存在,则输出command
将替换其内容。如果不存在,将创建它。
>>
:与相同>
,但如果目标文件存在,则附加新数据。
command >> out.txt
如果out.txt
存在,则command
其中的任何内容之后,的输出都将附加到它的后面。如果不存在,将创建它。
&>
,>&
,>>&
和&>>
:(非标准)。重定向标准错误和标准输出,分别替换或附加。
command &> out.txt
标准错误和的标准输出都command
将保存在中out.txt
,覆盖其内容,或者如果不存在则创建它。
command &>> out.txt
如上所述,除了如果out.txt
存在,输出和错误command
将被附加到它。
该&>
变种起源于bash
,而>&
变异来自CSH(几十年前)。它们都与其他POSIX Shell运算符冲突,因此不应在可移植sh
脚本中使用。
<<
:此处文档。它通常用于打印多行字符串。
command << WORD
Text
WORD
在这里,command
直到找到下一个出现将采取一切WORD
,Text
在上面的例子中,作为输入。尽管WORD
通常EoF
是其首字母缩写或其变体,但是它可以是您喜欢的任何字母数字(不仅是)字符串。当WORD
被引用,在这里文档中的文本字面上处理,(例如在变量)没有进行扩展。如果未引用,变量将被扩展。有关更多详细信息,请参见bash手册。
如果要将管道的输出command << WORD ... WORD
直接传送到另一个命令或多个命令中,则必须将管道与放在同一行<< WORD
,您不能将其放在终止WORD之后或后面的行中。例如:
command << WORD | command2 | command3...
Text
WORD
<<<
:此处字符串,类似于此处文档,但用于单行。这些仅存在于Unix端口或rc(它起源于此),zsh,ksh,yash和bash的某些实现中。
command <<< WORD
给出的随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
其他一些运算符(>&-
,x>&y
x<&y
)可用于关闭或复制文件描述符。有关它们的详细信息,请参阅您的shell的手册中的相关部分(这里例如对于bash)。
那只涵盖了像Bourne这样的shell的最常见的运算符。一些外壳具有自己的一些其他重定向操作符。
Ksh,bash和zsh也具有结构<(…)
,>(…)
和=(…)
(zsh
仅后者)。这些不是重定向,而是进程替换。
bash
。这可以作为规范问答,以解决各种“这奇怪的事情做什么”问题,其中大部分来自bash用户。我希望其他人会介入并回答非bash shell,但是突出显示特定于bash的外壳很有意义。我必须检查一下,但我不知道它们在我头顶上。
&>
,,>>>
和<<<
均为非posix,就像在此处文档的名称中引用非唯一的非字母字符一样。这个答案还讨论甚少如何他们的工作-例如,它几乎是有害无益谈论一个简单的命令和命令没有解释这些是什么,以及如何壳决定。
false
),退出码0表示成功(不是true
)。一直以来都是这样,而且很标准。非0的退出代码表示在我所知道的每个环境中均存在错误。
刚了解I / O重定向(<
和>
)的Unix初学者经常尝试类似的事情
命令 … input_file > the_same_file
要么
命令 …< 文件 > the_same_file
或几乎相等地
猫文件 | 命令 ...> the_same_file
(grep
,sed
,cut
,sort
,和spell
是人们都禁不住在这样的结构可以使用的命令的例子。)用户都惊讶地发现,这些情况导致文件变空。
在bash(1)的Redirection部分的第一句话中可以找到一个在其他答案中似乎未提及的细微差别:
在执行命令之前,可以 使用由Shell解释的特殊符号来重定向其输入和输出。
前五个词应为粗体,斜体,下划线,放大,闪烁,红色和带有图标标记,以强调以下事实:在执行命令之前,外壳程序执行了请求的重定向 。还要记住
输出重定向导致文件…打开以进行写入…。如果文件不存在,则创建该文件;如果确实存在,则将其截断为零大小。
因此,在此示例中:
sort roster > roster
在程序开始运行roster
之前,shell打开文件进行写入,将其截断(即丢弃其所有内容)sort
。自然,什么也做不了来恢复数据。
可能天真地希望
tr "[:upper:]" "[:lower:]" < poem > poem
可能会更好。由于外壳程序处理从左到右的重定向,因此在打开它进行写入(对于标准输出)之前,它会打开poem
以进行读取(对于tr
的标准输入)。但这没有帮助。即使此操作序列产生两个文件句柄,它们都指向同一文件。当shell打开文件进行读取时,内容仍然存在,但是在执行程序之前它们仍然会被破坏。
解决方案包括:
检查您正在运行的程序是否具有自己的内部功能,以指定输出的位置。这通常用-o
(或--output=
)令牌表示。尤其是,
sort roster -o roster
大致相当于
sort roster > roster
除了,在第一种情况下,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
有关更多信息,请参见此答案。
这让我感到完全惊讶: 语法错误说:
[这些解决方案中的大多数]在只读文件系统上将失败,其中“只读”表示您
$HOME
将是可写的,但/tmp
将是只读的(默认情况下)。例如,如果您具有Ubuntu,并且已启动到故障恢复控制台,通常就是这种情况。同样,here-document运算符<<<
也不会在那里工作,因为它需要/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
上面的答案。在语法上打开文件描述符命名文件的输入和输出,但不截断它-排序的组合
和。注意:有些程序(例如和)可能拒绝在这种情况下运行,因为它们可以检测到输入和输出是同一文件。请参阅此答案
,以进行上述讨论,以及一个脚本,如果保证您的命令产生的输出数据量与input相同或更少,则可以使该答案起作用。
警告:我尚未测试过Peter的脚本,所以我不作保证。n<> file
n
n<
n>
cat
grep
这已成为U&L的热门话题。在以下问题中得到解决:
iconv
用转换后的输出替换输入文件?shuf file > file
留下一个空文件?sort
命令给我一个空文件?tr
标准输出重定向到文件……这还不包括超级用户或Ask Ubuntu。在此答案中,我已经结合了上述问题的答案中的很多信息,但不是全部。(即,有关更多信息,请阅读上面列出的问题及其答案。)
PS:我与上面引用的博客没有任何隶属关系。
/tmp
。
dd … conv=notrunc
和,1<>
答案永远都不会截断输出文件,因此,如果命令的输出小于输入(例如grep
),则文件末尾会剩下一些原始字节。而且,如果输出大于输入较大(如cat -n
,nl
或(潜在的)grep -n
),还有就是你读它之前覆盖旧数据的风险。
;
,&
,(
和)
请注意,terdon答案中的某些命令可能为空。例如,你可以说
command1 ;
(无command2
)。这相当于
command1
(即,它只是command1
在前台运行并等待其完成。相比之下,
command1 &
(不带command2
)将command1
在后台启动,然后立即发出另一个shell提示。
相比之下,command1 &&
,command1 ||
,并command1 |
没有任何意义。如果键入其中一个,则外壳程序将(可能)假定命令继续到另一行。它将显示辅助(继续)shell提示符,通常将其设置为>
,并继续阅读。在shell脚本中,它将仅读取下一行并将其追加到已经读取的行中。(请注意:这可能不是您想要发生的事情。)
注意:某些Shell的某些版本可能会将不完整的命令视为错误。在这种情况下(或者,实际上,在任何情况下您都有一条长命令),您可以\
在行的末尾加上反斜杠(),以告诉Shell继续在另一行上读取命令:
command1 && \
command2
要么
find starting-directory -mindepth 3 -maxdepth 5 -iname "*.some_extension" -type f \
-newer some_existing_file -user fred -readable -print
正如terdon所说,(
并且)
可以用来对命令进行分组。关于它们与该讨论“无关紧要”的说法值得商bat。Terdon答案中的某些命令可能是命令组。例如,
( command1 ; command2 ) && ( command3; command4 )
做这个:
command1
并等待其完成。command2
并等待其完成。然后,如果command2
成功,
command3
并等待其完成。command4
并等待其完成。如果command2
失败,请停止处理命令行。
括号外,|
绑定非常紧密,因此
command1 | command2 || command3
相当于
( command1 | command2 ) || command3
和&&
并||
结合除更紧;
,因此
command1 && command2 ; command3
相当于
( command1 && command2 ) ; command3
即,command3
无论command1
和和或的退出状态如何,都将执行command2
。
;
它本身(或在命令之前没有命令)是语法错误,而不是空语句。因此; ;
是错误的。(恕我直言,这是新用户的常见陷阱)。另外:;;
是一个特殊的分隔符,用于case
语句。
;
,&&
,||
,&
,和|
,是错误的,如果他们似乎什么也没有之前他们。另外,terdon ;;
在回答中(简短地)说。
linebreak
POSIX Shell语法中令牌的用法之前,我曾写过这篇文章。因此,可以肯定地说所有POSIX兼容的外壳都可以接受它们。我坚持不作一般性声明。如果找到足够旧的POSIX之前的外壳,例如实际的Bourne外壳或更旧的外壳,则所有投注均无效。