shell 的控制和重定向操作符是什么?

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 中的工作方式。

A. 控制操作员

POSIX 定义

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

&   &&   (   )   ;   ;;   <newline>   |   ||
Run Code Online (Sandbox Code Playgroud)

|&在bash。

一个!没有控制操作员,但一个保留字。它成为算术表达式和测试结构内部的逻辑 NOT [否定运算符] (同时仍需要空格分隔符)。

A.1 列出终止符

  • ; : 无论第一个命令的结果如何,都会在另一个命令完成后运行一个命令。

    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是可选的。

A.2 逻辑运算符

  • && : 用于构建 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)

A.3 管道操作员

  • |: 管道运算符,它将一个命令的输出作为输入传递给另一个。从管道操作符构建的命令称为管道

     command1 | command2
    
    Run Code Online (Sandbox Code Playgroud)

    打印的任何输出command1都作为输入传递给command2.

  • |&: 这是2>&1 |bash 和 zsh的简写。它将一个命令的标准输出和标准错误作为输入传递给另一个命令。

    command1 |& command2
    
    Run Code Online (Sandbox Code Playgroud)

A.4 其他列表标点

;;仅用于标记case 语句的结束。Ksh、bash 和 zsh 还支持;&进入下一个案例和;;&(不在 ATT ksh 中)继续并测试后续案例。

()用于对命令进行分组并在子外壳中启动它们。{并对}命令进行分组,但不要在子 shell 中启动它们。有关shell 语法中各种类型的括号、方括号和大括号的讨论,请参阅此答案

B. 重定向操作符

重定向运算符的 POSIX 定义

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

<     >     >|     <<     >>     <&     >&     <<-     <>
Run Code Online (Sandbox Code Playgroud)

这些允许您控制命令的输入和输出。它们可以出现在简单命令中的任何位置,也可以跟随命令。重定向按照它们出现的顺序进行处理,从左到右。

  • < : 为命令提供输入。

    command < file.txt
    
    Run Code Online (Sandbox Code Playgroud)

    以上将commandfile.txt.

  • <>: 同上,但文件以读+写模式打开,而不是只读模式:

    command <> file.txt
    
    Run Code Online (Sandbox Code Playgroud)

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

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

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

    command > out.txt
    
    Run Code Online (Sandbox Code Playgroud)

    以上将把输出保存commandout.txt. 如果文件存在,它的内容将被覆盖,如果它不存在,它将被创建。

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

    command >out.txt 2>error.txt
    
    Run Code Online (Sandbox Code Playgroud)

    在上面的例子中,>将重定向标准输出并2>重定向标准错误。输出也可以使用1>但是重定向,因为这是默认值,1通常会省略 并且它简单地写为>.

    因此,运行commandfile.txt并保存其输出out.txt和任何错误消息在error.txt你可以运行:

    command < file.txt > out.txt 2> error.txt
    
    Run Code Online (Sandbox Code Playgroud)
  • >|: 与 相同>,但会覆盖目标,即使外壳已配置为拒绝覆盖(使用set -Cset -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将获取所有内容,直到找到下一个出现的WORDText在上例中,作为输入。虽然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仅后一个)。这些不是重定向,而是进程替换

  • 可能值得注意的是,并非所有 shell 都是相同的,并且特别强调了 bash 特定的功能。 (2认同)
  • @Arc676 不,它们不会评估为真或假,这是完全不同的上下文。这只是意味着非 0 的退出值表示有问题(不是“假”),退出代码为 0 表示成功(不是“真”)。这一直是方式,是相当标准的。非 0 退出代码表示在我知道的每个环境中都存在错误。 (2认同)

Sco*_*ott 74

关于“>”的警告

刚刚了解 I/O 重定向(<>)的Unix 初学者经常尝试像

命令... input_file > the_same_file

或者

命令... <文件     > the_same_file

或者,几乎等效地,

文件| 命令……> the_same_file

grepsedcutsortspell是人们倾向于在此类结构中使用的命令示例。)用户惊讶地发现这些方案会导致文件变空。

bash(1)重定向部分的第一句中,可以发现其他答案中似乎没有提到的细微差别:

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

前五个字应为粗体、斜体、下划线、放大、闪烁、红色,并标有 红色三角形中的感叹号图标,强调 shell 在执行命令之前执行请求的重定向这一事实 。还要记住

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

  1. 所以,在这个例子中:

    commandinput_file > the_same_file

    在程序开始运行roster之前,外壳程序打开文件进行写入、截断它(即丢弃其所有内容)sort。自然,无法采取任何措施来恢复数据。

  2. 人们可能会天真地期望

    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/ exemacspicovi/ 之类的编辑器vim 允许用户编辑文本文件并将编辑后的文本保存在原始文件中。请注意,ed(至少)可以以非交互方式使用。

    • vi有一个相关的功能。如果您键入,它会将编辑缓冲区的内容写出到,读取输出,并将其插入缓冲区(替换原始内容)。:%!commandEntercommand
  • 简单但有效:

    命令... input_file > temp_file   && mv temp_file  input_file

    这有一个缺点,如果input_file是一个链接,它将(可能)被一个单独的文件替换。此外,新文件将由您拥有,并具有默认保护。特别是,这会带来文件最终成为世界可读的风险,即使原始文件input_file不是。

    变化:

    • commandinput_file > temp_file && cp temp_file input_file && rm temp_file
      这仍然(可能)让temp_file世界可读。更好的是:
    • cp input_file temp_file && commandtemp_file > input_file && rm temp_file
      这些保留文件的链接状态、所有者和模式(保护),可能以两倍的 I/O 为代价。(您可能需要使用 like-a-pon选项cp 来告诉它保留属性。)
    • commandinput_file > temp_file &&
      cp --attributes-only --preserve=all input_file temp_file &&
      mv temp_file input_file
      (为了可读性而分成单独的行)这会保留文件的模式(并且,如果您是 root,则为所有者),但使其归您所有(如果您不是 root),并使其成为新的,单独的文件。
  • 这个博客 (文件的“就地”编辑)建议和解释

    { 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),则可以使用的替代方法。这些答案的优点是它们不需要任何可用空间(或者它们只需要很少的空间)。上面表格的答案 明确要求系统有足够的可用空间,以便能够同时保存整个输入(旧)文件和输出(新)文件;对于大多数其他解决方案(例如和)来说,这显然不是正确的。例外:可能需要大量可用空间,因为commandinput_file > temp_file && …sed -ispongesort … | dd …sort 在写入任何输出之前需要读取其所有输入,并且它可能会在临时文件中缓冲大部分数据(如果不是全部)。
  • 仅限高级用户:
    命令... input_file 1<> the_same_file
    可能相当于dd上面的答案。在语法上打开文件描述符命名文件的输入和输出,但不截断它-排序的组合 和。注意:在这种情况下,某些程序(例如和)可能会拒绝运行,因为它们可以检测到输入和输出是同一个文件。请参阅此答案 以了解上述讨论,以及如果您的命令保证产生与输入相同数量的输出数据或更少,则使此答案起作用的脚本。 警告:我没有测试过 Peter 的脚本,所以我不保证它。n<> filen n<n>catgrep

那么,问题是什么?

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

……这还不包括超级用户或 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 次

最近记录:

4 年,3 月 前