我经常在网上看到教程,这些教程将各种命令与不同的符号相连。例如:

command1 |  command2
command1 &  command2
command1 || command2    
command1 && command2


其他人似乎正在将命令连接到文件:

command1  > file1
command1  >> file1


这些是什么? ?他们叫什么?他们在做什么?还有更多吗?


有关此问题的元线程。.

#1 楼

这些被称为shell运算符,是的,它们更多。我将简要概述两个主要类(控制操作符和重定向操作符)中最常见的类,以及它们在bash shell中的工作方式。

A.控制运算符

POSIX定义


在shell命令语言中,是执行控制功能的令牌。
它是以下符号之一:

&   &&   (   )   ;   ;;   <newline>   |   ||
和bash中的|&

!不是控制运算符,而是保留字。它成为算术表达式内部和测试构造内部的逻辑NOT [求反运算符](同时仍需要空格分隔符)。

A.1列表终止符



;:不管第一个命令的结果如何,都将在另一个命令完成后再运行一个命令。完成后,将运行command1

不在字符串文字中或某些关键字之后的换行符不等于分号运算符。 command2分隔的简单命令列表仍然是列表-因为在执行之前,shell解析器中仍必须继续读取;分隔的简单命令之后的简单命令,而换行符可以分隔整个命令列表-或列表列表。区别是微妙的,但很复杂:由于外壳程序以前没有必要在换行符之后读取数据,因此换行符标志着外壳程序可以开始评估已经读取的简单命令的点,而;分号不会。


;:这将在后台运行命令,使您可以在同一外壳中继续工作。

command1 ; command2


在这里,&在后台启动,command1立即在前台运行,而无需等待command2退出。

command1之后的换行符是可选的。


A.2逻辑运算符



command1:用于构建AND列表,它允许您仅在另一个命令成功退出时才运行一个命令。

 command1 & command2


这里,仅在&&成功(如果其退出代码为0)之后,command2将在command1完成后运行。这两个命令都在前台运行。

也可以编写此命令

 command1 && command2


,或者如果忽略返回状态,则只需command1


if command1; then command2; fi:用于构建OR列表,它允许您仅在另一个命令未成功退出时运行一个命令。

if command1
then command2
else false
fi


此处,||仅在command2失败(如果返回的退出状态不是0)时运行。这两个命令都在前台运行。

也可以编写此命令

 command1 || command2


,或更短的形式command1

请注意,if ! command1; then command2; fi&&是左关联的;请参阅外壳程序逻辑运算符&&,||的优先级。有关更多信息。


||:这是保留字,用作“ not”运算符(但必须具有定界符),用于取消命令的返回状态-return如果命令返回一个非零状态,则返回0;如果返回状态0,则返回1。!实用程序也是逻辑非。算术表达式:

if command1
then true
else command2
fi



A.3管道运算符




test:管道运算符,它将一个命令的输出作为输入传递给另一命令。由管道运算符生成的命令称为管道。

! command1

[ ! a = a ]


|打印的任何输出都作为输入传递到command1


command2:这是bash和zsh中|&的简写。它会将一个命令的标准输出和标准错误作为另一个命令的输入传递。

$ echo $((!0)) $((!23))
1 0



A.4其他列表标点符号

2>&1 |仅用于标记案例说明的结尾。 Ksh,bash和zsh还支持;;进入下一个案例,并支持;&(不在ATT ksh中)继续测试后续案例。

;;&(用于对命令进行分组并启动它们在一个子shell中。 ){还会对命令进行分组,但不要在子shell中启动它们。请参阅此答案,以获取有关Shell语法中各种类型的括号,括号和花括号的讨论。

B。重定向运算符

POSIX重定向运算符的定义


在shell命令语言中,是执行重定向功能的令牌。它是以下符号之一:

 command1 | command2



这些符号使您可以控制命令的输入和输出。它们可以出现在简单命令内的任何位置,也可以跟随命令。重定向按照从左到右的顺序显示。



}:为命令提供输入。

command1 |& command2


上面的代码将对<的内容执行command


file.txt:与上面相同,但是文件以读写模式而不是只读方式打开:

<     >     >|     <<     >>     <&     >&     <<-     <>


如果文件不存在,它将被创建。

该运算符很少使用,因为命令通常仅从其stdin中读取,尽管在许多特定情况下它可以派上用场。


<>:将命令的输出定向到文件中。

command < file.txt


上面将把>的输出另存为command。如果文件存在,则其内容将被覆盖;如果文件不存在,则将创建它。

此运算符还经常用于选择是将某些内容打印到标准错误还是标准输出:

command <> file.txt


在上面的示例中, out.txt将重定向标准输出,而>将重定向标准错误。也可以使用2>重定向输出,但是由于这是默认设置,因此通常会省略1>并将其写为1

因此,要在>上运行command并将其输出保存在file.txt和任何其他格式中您将运行out.txt中的错误消息:

command > out.txt



error.txt:与>|相同,但即使配置了外壳,也会覆盖目标拒绝覆盖(使用>set -C)。

command >out.txt 2>error.txt


如果存在set -o noclobber,则out.txt的输出将替换其内容。如果不存在,将创建它。


command:与>>相同,但如果目标文件存在,则附加新数据。

command < file.txt > out.txt 2> error.txt


如果>存在,则将out.txt的输出添加到它的后面,不管它中已有什么。如果不存在,将创建它。


command :(按POSIX规范)当被数字(>&)或右侧的1>&2包围(-)时,仅重定向一个文件描述符或将其关闭(1>&-)。 />
>&-后跟文件描述符编号是重定向文件描述符的可移植方式,而>&是可移植方法关闭文件描述符。

重定向是文件,请阅读下一个条目。


>&->&&>>>& :(也请阅读上文)分别重定向标准错误和标准输出,替换或追加。

&>>的内容将保存在command中,覆盖其内容或在不存在的情况下创建。

command >| out.txt


如上所述,除了如果存在out.txt,输出和错误out.txt将被附加到它。

command变体起源于&>,而bash变体起源于csh(几十年前)。它们都与其他POSIX Shell运算符冲突,因此不应在可移植的>&脚本中使用。


sh:此处文档。它通常用于打印多行字符串。

command >> out.txt


这里,<<将占用所有内容,直到在上例中找到下一个出现的commandWORD为止,输入。尽管Text通常是WORD或其变体,但它可以是您喜欢的任何字母数字(不仅是)字符串。引用EoF时,此处文档中的文本将按字面意义处理,并且不执行任何扩展(例如,对变量进行扩展)。如果未引用,变量将被扩展。有关更多详细信息,请参见bash手册。

如果要将WORD的输出直接输送到另一个命令中,则必须将管道与command << WORD ... WORD放在同一行上,则不能将其放在终止WORD之后或随后的行中。例如:

command &> out.txt



<< WORD:这里的字符串,类似于这里的文档,但用于单行。这些仅存在于Unix端口或rc(它起源于此),zsh,ksh,yash和bash的某些实现中。

command &>> out.txt


<<<给出的任何值都会展开,并将其值作为输入传递到WORD。这通常用于将变量的内容作为输入传递给命令。例如:

 command << WORD
     Text
 WORD



其他一些运算符(command>&- x>&y)可用于关闭或重复文件描述符。有关它们的详细信息,请参见您的Shell手册的相关部分(例如bash)。

仅涵盖最常见的Bourne式shell运算符。一些shell拥有自己的一些其他重定向操作符。

Ksh,bash和zsh还具有x<&y<(…)>(…)的构造(仅后者在=(…)中)。这些不是重定向,而是进程替换。

评论


可能值得一提的是,并非所有shell都相等,并且特别强调了bash特定的功能。

– Greg Hewgill
2014年10月6日,下午2:34

@GregHewgill是的,我说我正在讨论有关bash的问题,这使我感到疲倦。这可以作为规范问答,以解决各种“这奇怪的事情做什么”问题,其中大多数来自bash用户。我希望其他人会介入并回答非bash shell,但是突出显示特定于bash的外壳很有意义。我必须检查一下,但我不知道它们在我的头上。

– terdon♦
2014年10月6日在2:51



&>,>>>和<<<都是非正则表达式,就像在此处文档名称中对非唯一的非字母字符的引用一样。这个答案也很少讨论它们的工作原理,例如,谈论一个简单的命令和一个命令而不解释它们是什么以及shell是如何决定的,这几乎比没用还糟。

–mikeserv
14-10-6在3:23



@mikeserv谢谢。他们虽然在bash和zsh上工作。我不知道在该列表中什么是真正的bash特定内容。我应该仔细检查一下,并添加每个可以使用的外壳,但这需要首先找出。

– terdon♦
2014年10月6日,下午3:26

@ Arc676不,它们的取值不能为true或false,这是完全不同的上下文。这仅表示退出值为非0表示问题(不是false),退出码为0表示成功(不是true)。一直以来都是这样,而且很标准。非0的退出代码表示在我所知道的每个环境中均存在错误。

– terdon♦
17年9月15日在14:54

#2 楼

关于'>'的警告

刚学会I / O重定向(<>)的Unix初学者
经常尝试类似

command … input_file > the_same_file




command … < file     > the_same_file


或几乎等同地,

cat file | command … > the_same_file


grepsedcutsortspell是命令的示例,人们很想在这些结构中使用这些命令。)
用户惊讶地发现这些情况导致文件变空。

在bash(1)的Redirection(重定向)部分的第一句话中可以找到潜伏在另一个答案中未提及的细微差别:


在执行命令之前,可以使用由Shell解释的特殊符号来重定向其输入和输出。


前五个单词应为粗体,斜体,下划线,放大,闪烁,
红色,并带有图标标记,
以强调事实是,在执行命令之前,外壳程序会执行请求的重定向。
还请记住


输出的重定向导致文件…被打开以进行写入…。
如果文件不存在,则会创建该文件;如果确实存在,则将其截断为零。




因此,在此示例中:

 sort roster > roster
 


roster程序启动之前,外壳程序打开sort文件进行写入,并对其进行截断(即丢弃其所有内容)。运行。
自然地,什么也做不了,无法恢复数据。


人们可能会天真地希望
重写“> tr "[:upper:]" "[:lower:]" < poem > poem

可能更好。
由于外壳程序处理从左到右的重定向,
它将打开poem进行读取(用于tr的标准输入)
在打开以进行写入之前(用于标准输出)。
但这没有帮助。
即使此操作序列产生两个文件句柄,
它们都指向同一文件。
当外壳程序打开文件进行读取时,内容仍然存在,但在执行程序之前它们仍然被破坏。


那么,该怎么办?

解决方案包括:




检查是否您正在运行的程序
具有自己的内部功能,可以指定输出的位置。
这通常由-o(或--output=)令牌指示。
尤其是

 sort -o roster roster
 


大致等同于
<
 sort roster > roster
 


除外,在第一种情况下,sort程序打开输出文件。
它很聪明,不用打开输出文件,直到读完所有输入文件后。

类似地,至少某些版本的sed具有一个-i(就地编辑)选项
,可用于将输出写回输入文件
(同样,在读取所有输入之后)。
ed / exemacspicovi / vim之类的编辑器
允许用户编辑文本文件并将编辑后的文本保存在原始文件中。
请注意,ed(至少)可以非交互式使用。



vi具有相关功能。
如果键入:%!command Enter,
它将把编辑缓冲区的内容写到command,读取输出,
并将其插入缓冲区(替换原始内容)。
/>


简单但有效:

command … 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
这些保留了文件的链接状态,所有者和模式(保护),
可能以两倍于/ O。
(您可能需要在-a上使用诸如-pcp之类的选项来告诉它保留属性。)

command … input_file > temp_file && cp --attributes-only --preserve=all input_file temp_file && mv temp_file input_file
(分成几行仅出于可读性)
保留文件的模式(如果您是root用户,则是所有者),
但使其归您所有(如果您不是root用户),并使其一个新的单独文件。



此博客
(文件的“就地”编辑)建议并说明

{ rm input_file  &&  command … > input_file; } < input_file


这要求command能够处理标准输入
(但几乎所有过滤器都可以)。
博客本身称此为危险行为,不鼓励使用。
这还将创建一个新的单独文件(未链接到任何文件),
由您拥有并具有默认权限。


moreutils软件包具有一个称为sponge的命令:

command … input_file | sponge the_same_file


有关更多信息,请参见此答案。


这让我感到完全惊讶:
syntaxerror说:


[大多数解决方案]在只读文件系统上将失败,
其中“只读”表示您的$HOME将为可写,
,但/tmp将为只读(默认情况下)。
例如,如果您具有Ubuntu,并且已启动到故障恢复控制台,
通常就是这种情况。
另外,此处文档运算符<<<也不在这里工作,
因为它需要/tmp进行读取/写入
,因为它也会在其中写入一个临时文件。
(参见此问题包括strace的'd输出)


在这种情况下,以下方法可能会起作用:



仅适用于高级用户:
如果保证您的命令产生相同数量的输出数据, br />由于有输入(例如,sorttr,而没有-d-s选项),
您可以尝试

command … input_file | dd of=the_same_file conv=notrunc


请参见此答案
和此答案以获取更多信息,
包括对上述内容的解释,以及可行的替代方法
,如果保证您的命令能够产生与输入相同数量的输出数据,则为
或更少(例如,grepcut)。
这些答案的优点是它们不需要任何可用空间
(或者只需要很少的空间)。
以上形式的答案
command … input_file > temp_file && … 对于其他大多数解决方案(例如sed -isponge)也显然不是这样。
例外:sort … | dd …可能需要大量的可用空间,
因为sort需要先读取其所有输入,然后才能写入任何输出,
它可能缓冲大多数(如果不是全部)数据

仅适用于高级用户:

command … input_file 1<> the_same_file


可能等同于上面的dd答案。
n<> file语法在文件描述符n上为输入和输出打开命名文件,而不会截断–
n<
n>的组合。
注意:某些程序(例如catgrep)在这种情况下可能会拒绝运行
,因为它们可以检测到输入和输出是同一文件。
查看此答案
对于上述内容,
以及一个脚本,如果您的命令得到保证,该脚本将使该答案有效
产生与输入相同或更少的输出数据量。
警告:我还没有测试过Peter的脚本,所以我不保证。

那么,问题是什么?

这是U&L的热门话题;它在以下问题中得到解决:


是否可以就地修改文件?
如何使iconv用转换后的输出替换输入文件?
为什么shuf file > file命令会留下一个空文件?
我可以在Linux中读写同一文件而不覆盖它吗?
重定向到与该命令处理的源文件相同的文件
为什么此sort命令给我一个空文件?
tr stdout重定向到文件中
grep:输入文件'X'也是输出
重定向程序操作符是否打开文件描述符并行吗?
重定向不覆盖文件,而只是产生一个空白文件。

...这不算超级用户或Ask Ubuntu。
我已将上述问题的答案中的很多信息纳入其中
,但不是全部。
(例如,有关更多信息,请阅读上面列出的问题及其答案。)

P.S.我与上面引用的博客没有任何关系。

评论


由于这个问题不断出现,我想我会尽力写一个“规范的答案”。我应该在这里发布(或者从其他一些流量较大的问题链接到它),还是应该将其移至实际上引发此问题的问题之一?另外,这也许是应该合并问题的情况吗?

–斯科特
15年2月21日在20:43

/ tmp一个目录,可用于需要放置临时文件的位置的应用程序。应允许应用程序在此目录中创建文件,但不应假定此类文件在应用程序调用之间被保留。

–mikeserv
15年2月21日在22:05

@mikeserv:是的,(1)我引用了syntaxerror,(2)我说我很惊讶。我以为,如果任何东西都是可读写的,那就是/ tmp。

–斯科特
15年2月21日在22:09

好吧,@ syntaxerror所说的事情是双重奇怪的,因为,正如我认为的那样,破折号将是Ubuntu上的默认恢复外壳,它不仅不理解<<<这里的字符串,而且还为<<这里的文档获取匿名管道,并且完全不会为此而用$ {TMPDIR:-/ tmp}。有关此处文档处理的演示,请参见此内容。同样为什么输出相同或更少的警告?

–mikeserv
2015年2月21日在22:15



@mikeserv:好吧,dd…conv = notrunc和1 <>的答案永远不会截断输出文件,因此,如果命令的输出小于输入(例如grep),则会有一些原始字节留在文件末尾。而且,如果输出大于输入(例如cat -n,nl或(可能)grep -n),则存在在读取之前覆盖旧数据的风险。

–斯科特
2015年2月21日在23:32



#3 楼

关于;&()的更多观察结果





请注意,terdon答案中的某些命令可能为空。
例如,您可以说

 command1 ;
 


(无command2)。这等效于

 command1
 


(即,它只是在前台运行command1并等待
相比之下,

 command1 &
 


(没有command2)将在后台启动command1
,然后立即发出另一个shell提示。


相反,command1 &&command1 ||command1 |没有任何意义。
如果键入其中之一,
shell将(可能)假定命令继续到另一行。
它将显示辅助(continuation)shell提示,
通常已设置到>并继续阅读。
在shell脚本中,它将仅读取下一行
并将其附加到已经阅读的内容上。
(请注意:这可能不是

注意:某些shell的某些版本可能会将不完整的命令视为er罗尔斯。
在这种情况下(或者实际上,如果您有长命令),
您可以在行尾添加反斜杠(\
告诉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



评论


完美,+ 1!我说过它们不相关,因为我不想涉及太多细节。我想要一个可以作为新手快速备忘单的答案,这些新手想知道各种命令结尾处所有奇怪的花样是什么。我并不是要暗示它们没有用。感谢您添加所有这些内容。

– terdon♦
2014年10月6日18:07

我担心“临界质量”问题-如果我们发布关于shell的所有内容,我们将得到自己的Bash参考手册的TL; DR版本。

– G-Man说“恢复莫妮卡”
2014年10月6日18:09



还值得一提:与C系列语言不同,本身(或前面没有命令)是语法错误,而不是空语句。因此; ;是一个错误。 (恕我直言,这是新用户的常见陷阱)。另外:;;是用于case语句的特殊分隔符。

–muru
2014年10月6日18:10



@muru:好点,但是让我们概括一下。如果出现在命令之间的任何控制运算符:;,&&,||,&和|,都是错误的,如果它们之前没有出现任何内容。另外,terdon谈到了; (简短地)回答。

– G-Man说“恢复莫妮卡”
2014年10月6日18:31



@Wildcard:好的,我知道你来自哪里。关键词是“可以”。我只是说我不保证所有shell都将接受此类构造(即YMMV)。显然,我是在知道POSIX Shell语法中使用换行标记之前写的。因此,可以肯定地说所有POSIX兼容的外壳都可以接受它们。我坚持不作一般性声明。如果您找到了足够旧的POSIX之前的外壳,例如实际的Bourne外壳或更旧的外壳,那么所有选择都将关闭。

– G-Man说“恢复莫妮卡”
16-3-30在5:35