在bash中做什么&>做什么?

mer*_*011 43 bash

我正在查看预提交钩子,并发现了以下行,因为我想知道为什么我1在提交后总是在我的目录中调用一个empy文件.

git status 2&>1 > /dev/null
Run Code Online (Sandbox Code Playgroud)

我认为其目的是写下以下内容,并对其进行了更正.

git status 2>&1 > /dev/null
Run Code Online (Sandbox Code Playgroud)

但是,我很好奇下面的语法完全是什么,所以我查看了手册页.

git status 2&>1
Run Code Online (Sandbox Code Playgroud)

这是手册页.

  Redirecting Standard Output and Standard Error
      This  construct allows both the standard output (file descriptor 1) and
      the standard error output (file descriptor 2) to be redirected  to  the
      file whose name is the expansion of word.

      There  are  two  formats  for  redirecting standard output and standard
      error:

             &>word
      and
             >&word

      Of the two forms, the first is preferred.  This is semantically equiva?
      lent to

             >word 2>&1
Run Code Online (Sandbox Code Playgroud)

但是,这个手册页暗示两者是等价的,似乎并非如此.

有人可以澄清手册页并准确解释这种语法发生了什么吗?

M.M*_*M.M 51

我们在这里使用的运营商是:

  • > 语法:file_descriptor opt > file_name
  • >&语法:file_descriptor opt >& file_descriptor
  • &>语法:&> file_name

如果省略文件描述符,则0输入的默认值为(stdin),输出的默认值为1(stdout). 2意思是斯特德尔.

所以我们有:

  • >name表示1>name- 将stdout重定向到文件name
  • &>name意味着1>name 2>name- 将stdout和stderr重定向到文件name

所以当你写作时name,它就是1>name 2>name,即

  • 第一个name实际上作为参数传递给git status 2&>1.
  • stdout被重定向到名为的文件git status 2 1>1 2>1(不是文件描述符1)
  • stderr被重定向到名为的文件 2

这个命令实际上应该创建一个文件调用git status,其内容是结果1- 即调用的文件的状态1可能是"你的分支是最新的,没有提交,工作目录清理",假设你实际上没有跟踪一个名为的文件1.

  • 你好,我认为`&> name`不等同于`1>name 2>name`,它等同于`1>name 2>&1`。请参阅 [man bash](https://linux.die.net/man/1/bash) 中的“重定向标准输出和标准错误”部分 (3认同)
  • @yundongxu你链接到的页面(也在问题中引用)说`&> name`与`> name 2>&1`相同.这与`1> name 2>&1`相同,因为`>`运算符是这样定义的.我不确定你想说什么. (2认同)
  • 晚会晚了,但是供以后的读者参考:`&> file`等同于`1> file 2>&1`。执行“ 1>文件2>文件”将不起作用,因为该文件在不同的文件描述符下打开了两次。文件中的文本会混乱(不是确定性的)。要以可移植的方式重定向STDOUT和STDERR,请使用`> file 2>&1`(同样,顺序很重要,因此OP建议的命令只会将错误重定向到文件并将标准输出重定向到/ dev / null)。 (2认同)

Gab*_*les 15

\n

关于 bash 中的重定向的所有内容

\n
\n

bash 中有什么作用&>?

\n
\n

关于错误的git status 2&>1 > /dev/null...

\n
\n

有人可以澄清手册页并准确解释此语法发生了什么吗?

\n
\n

我可以。我也试图理解 bash 语法的各种行为和位,所以让我们浏览一下有问题的语句和一些示例,以便一起获得更好的理解。

\n

快速总结

\n
    \n
  1. 如果您只想了解所讨论的奇怪/错误语法的完整含义: git status 2&>1 > /dev/null,请直接跳到下面的第 1 节。
  2. \n
  3. 如果您只是基于问题本身的标题:“&> 在 bash 中做什么?”,请直接跳到下面的第 2 部分。
  4. \n
  5. 如果您想快速参考 bash 中正确重定向用法的摘要,例如通过将 stderr 重定向到 stdout ,或者通过or2>&1将 stdout 重定向到文件,或者通过 重定向 stderr 到文件,或者将stdout和stderr都重定向到通过或文件,只需查看下面的第 3 节:“3. bash 中正确重定向的摘要”。>file1>file2>file&>file>file 2>&1
  6. \n
  7. 如果您只是想测试您的 bash 知识,了解奇怪的、冗余的、覆盖的或其他非标准的重定向用法,请查看下面的第 4 节。
  8. \n
\n

1. 声明细目git status 2&>1 > /dev/null

\n

在声明中

\n
git status 2&>1 > /dev/null\n
Run Code Online (Sandbox Code Playgroud)\n

2有 3 个独立的部分像这样分解(尽管这既不直观也不明显,特别是考虑到和&>1in之间没有空格2&>1):

\n
git status 2    # statement 1\n&>1             # statement 2\n> /dev/null     # statement 3\n
Run Code Online (Sandbox Code Playgroud)\n

然而,如果您有以下情况,那么它们与上面的情况有很大不同。我们稍后将在本答案中讨论这些内容:

\n
# separates into **two separate statements**: `2` and `&>1`\n2&>1  # `2` is NOT part of `&>1`\n\n# these are all **single statements**, but mean very different things (more on\n# these below)\n2>&1\n2>1\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. 语句 1 ( git status 2) 运行git status 2。这里,2是传递给 的参数git status。由于它不是有效选项,因此git status假定它是路径,可以是文件或目录。2因此,git 返回所有名为 的文件或名为 的目录中的所有文件的状态2。如图man git status所示,指定路径的更好方法是将选项与路径分开--,如下所示:

    \n
    git status -- 2  # 2 is a file or folder here\n
    Run Code Online (Sandbox Code Playgroud)\n

    显然,这不是您想要做的。:)

    \n
  2. \n
  3. 语句 2 ( &>1) 将 stdout 和 stderr 重定向到名为1. 这显然也不是您想要做的。:) 该&>file语法将所有 stdout 和 stderr 重定向到file. 您可以在 bash 在线手册中阅读相关内容,或者通过运行man bash和 并搜索名为“重定向标准输出和标准错误”的部分。稍后我们将详细讨论本手册的这一部分。

    \n

    请注意,&>1(在您的问题中写为2&>1--两个单独的语句:2and &>1)与的语法或概念不同2>&1。后者2>&1将文件描述符2(stderr) 重定向到文件描述符1(stdout),其中 3 个主要文件描述符是:

    \n
      \n
    1. 0=stdin
    2. \n
    3. 1=stdout
    4. \n
    5. 2=stderr
    6. \n
    \n
  4. \n
  5. 语句 3 ( > /dev/null):这就是事情开始变得棘手的地方。也可以不加空格地写成>/dev/null。它将 stdout(文件描述符1)重定向到/dev/nullLinux 伪文件,该伪文件会丢弃写入其中的任何输出。写入>/dev/null与 完全相同1>/dev/null,因为如果未指定,则文件描述符1(stdout) 隐含为默认选项,如 bash 手册所述。在bash 手册的“重定向输出”部分中阅读更多相关信息。

    \n

    这里开始变得棘手有两个原因:

    \n
      \n
    1. 首先,因为这1意味着两个不同的东西:&>1一个是一个名为“1”的文件。这可能是用户的错误,但事实就是如此。在>/dev/null中有一个隐含的 1 1>/dev/null,它指的是文件描述符1,即stdout。
    2. \n
    3. 其次,这很棘手,因为您正在进行重定向覆盖,而最后一个仍然存在。&>1将 stdout和stderr 重定向到名为“1”的文件,然后1>/dev/null将 stdout 重定向到“/dev/null”文件。stdout 的后者重定向会覆盖前者,因此最终结果是stderr重定向到名为“1”的文件并stdout重定向到“/dev/null”文件。这也可以写成:\n
      # redirect stderr to a file named "1", and redirect stdout to the file\n# named "/dev/null" to discard it.\n2>1 1>/dev/null\n\n# same thing\n&>1 > /dev/null\n
      Run Code Online (Sandbox Code Playgroud)\n
    4. \n
    \n
  6. \n
\n

以上信息恳求更多解释,所以让我们解释一下&>更多,然后详细总结如何在 bash 中正确地进行重定向。

\n

2. 只是解决这个问题的标题:“ &>bash 中有什么作用?”

\n

正如 @Casey Jones在他(不幸的是)删除的答案中暗示的那样,我认为 bash 手册解释得最好:https ://www.gnu.org/software/bash/manual/bash.html#Redirecting-Standard-Output-and -标准错误

\n

从中我们了解到,所有 4 种语法几乎都是相同的,其中第一种语法比第二种语法更受推荐,而第三种语法是在 bash 之外执行此操作的更通用方法:

\n
# 4 ways in bash to redirect both stdout and stderr to `file`\n\n# 1. recommended in bash\n# Think of the `&` symbol here as meaning "1 AND 2", since it redirects\n# both stdout (file descriptor 1) AND stderr (file descriptor 2) to `file`\n&>file\n# 2. works, but `file` may NOT expand to a number or to `-`. \n>&file\n\n# 3. the universal way to do it in or outside of bash: first redirect stdout to\n# file, and then redirect stderr to stdout\n>file 2>&1\n# 4. exact same as 3 above\n1>file 2>&1\n
Run Code Online (Sandbox Code Playgroud)\n

因此,&>file意味着“将和 stdout 重定向 stderr到file”。

\n

以下是手册的措辞:

\n
\n

3.6.4 重定向标准输出和标准错误

\n

此结构允许将标准输出(文件描述符 1)和标准错误输出(文件描述符 2)重定向到名称为单词扩展的文件。

\n

重定向标准输出和标准错误有两种格式:

\n
&>word\n
Run Code Online (Sandbox Code Playgroud)\n

和

\n
>&word\n
Run Code Online (Sandbox Code Playgroud)\n

在这两种形式中,优选第一种。这在语义上等价于

\n
>word 2>&1\n
Run Code Online (Sandbox Code Playgroud)\n

使用第二种形式时,单词可能不会扩展为数字或 \xe2\x80\x98-\xe2\x80\x99。如果是这样,出于兼容性原因,其他重定向运算符将适用(请参阅下面的复制文件描述符)。

\n
\n

3. bash中正确重定向的总结

\n

文件描述符提醒:

\n
    \n
  1. 0=stdin
  2. \n
  3. 1=stdout
  4. \n
  5. 2=stderr
  6. \n
\n

您应该使用良好的、正确使用的语法:

\n
# 1. file descriptor redirection\n\n# redirect stderr to stdout\n2>&1\n# redirect stdout to stderr\n1>&2\n\n\n# 2. redirection to a file\n\n# 2.A. redirect stdout to `file`\n>file\n1>file  # (same thing; the 1 is implied above)\n\n# 2.B. redirect stderr to `file`\n2>file\n\n# 2.C. redirect BOTH stdout and stderr to `file` (4 ways)\n# Think of the `&` symbol in this 1st example as meaning "1 AND 2", since it \n# redirects both stdout (file descriptor 1) AND stderr (file descriptor 2)\n# to `file`\n&>file       # recommended in bash      <===\n>&file       # not recommended\n>file 2>&1   # universal and fine       <===\n1>file 2>&1  # exact same as just above <===\n
Run Code Online (Sandbox Code Playgroud)\n

例子:

\n
# print "hey 2 " to stdout.\n# - Note: the `"%s "` format specifier gets applied to each input argument\n#   thereafter. So, calling `printf "%s " "hello" "world"` is as though you had\n#   called `printf "%s %s " "hello" "world"`.\nprintf "%s " "hey" 2\n\n# redirect stdout to `file`; "file" now contains "hey 2 "\nprintf "%s " "hey" 2 >file\nprintf "%s " "hey" 2 1>file  # (same thing)\n\n# redirect stderr to `file`; "file" remains empty since no stderr was printed\nprintf "%s " "hey" 2 2>file\n\n# redirect BOTH stdout and stderr to `file`\nprintf "%s " "hey" 2 &>file\nprintf "%s " "hey" 2&>file        # don\'t do this! (same thing in this case, \n                                  # but looks awkward without the space before \n                                  # the `&`)\nprintf "%s " "hey" 2 >file 2>&1   # (same thing)\nprintf "%s " "hey" 2 1>file 2>&1  # (same thing)\n
Run Code Online (Sandbox Code Playgroud)\n

4. 我们将以“谜题”的形式呈现并教授和测试理解的“奇怪”例子

\n
# print "hey 2 " to stdout, redirecting stdout to a file named "1", then to "2",\n# then to "3", then to "4", then to "5". Ultimately, `>5` overrides all\n# previous stdout redirections, resulting in 5 files being created\n# (named "1", "2", "3", "4", and "5"), with **only file "5"** containing\n# the "hey 2 " text, and all other files being empty. Read the contents of all \n# files all at once with `grep \'\' *`.\nprintf "%s " "hey" 2 >1 >2 >3 >4 >5\n# OR, exact same thing:\nprintf "%s " "hey" 2 1>1 1>2 1>3 1>4 1>5\n\n# read the contents of all files so you can see that only file "5" above has\n# "hey 2 " in it\ngrep \'\' *\n\n# print "hey " to a file named "5", while also creating empty files\n# named "1", "2", "3", and "4". Note: `21>1` is a bit nonsensical in this case.\n# It redirects file descriptor 21 to a file named "1". That doesn\'t really do\n# anything. All the redirections thereafter redirect file descriptor 1 (stdout)\n# to a file with a number for a name, as specified. \nprintf "%s " "hey" 21>1 1>2 1>3 1>4 1>5\n\n# print "hey " to a file named "5", while creating empty files\n# named "1", "2", "3", and "4". stderr gets redirected to the file named "1",\n# but it also ends up empty since no stderr output was produced by this command.\nprintf "%s " "hey" 2>1 1>2 1>3 1>4 1>5\n\n# Print some error output to a file named "1", since stderr gets redirected to\n# it. Stdout gets ultimately redirected to a file named "5", but no stdout\n# output is printed since `--invalid_arg` is not a valid argument. The stderr\n# error message printed to the file named "1" is:\n#       bash: printf: --: invalid option\n#       printf: usage: printf [-v var] format [arguments]\nprintf --invalid_arg "%s " "hey" 2>1 1>2 1>3 1>4 1>5\n\n# print "hey 2 " to a file named "5", while also creating empty files\n# named "1", "2", "3", and "4". Initially, both stdout and stderr get\n# redirected to the file named "1", via `&>1`, but then the stdout redirection\n# gets overridden repeatedly until the last one that "sticks" is the stdout\n# redirection to the file named "5" via `1>5`.\nprintf "%s " "hey" 2 &>1 1>2 1>3 1>4 1>5\nprintf "%s " "hey" 2&>1 1>2 1>3 1>4 1>5     # (same as above, but even more\n                                            # awkward-looking)\nprintf "%s " "hey" 2&>1 >2 >3 >4 >5         # (same as above)\nprintf "%s " "hey" 2 &>1 >2 >3 >4 >5        # (same as above)\nprintf "%s " "hey" 2 >1 2>&1 >2 >3 >4 >5    # (same as above)\nprintf "%s " "hey" 2 1>1 2>&1 >2 >3 >4 >5   # (same as above; reminder: `1>1` \n        # redirects stdout (file descriptor 1) to a file named "1", whereas\n        # `2>&1` redirects stderr (file descriptor 2) to stdout \n        # (file descriptor 1); make sure you understand that by now)\n\n# (NOT the same as above! See this example & description previously a few\n# examples up)\nprintf "%s " "hey" 2>1 1>2 1>3 1>4 1>5\n
Run Code Online (Sandbox Code Playgroud)\n


Eta*_*ner 8

&>word(并>&word重定向两者stdout以及stderr扩展单词的结果.在上述情况下,即文件1.

2>&1将stderr(fd 2)重定向到stdout(fd 1)的当前值.(在stdout稍后重新定向行之前执行此操作不会达到您所期望的效果,并且会分割输出而不是将它们组合在一起,这是一个非常常见的shell脚本错误.对比这个>word 2>&1将两个fds组合成一个发送到同一位置的.)

$ { echo stdout; echo stderr >&2; }
stdout
stderr
$ { echo stdout; echo stderr >&2; } >/dev/null
stderr
$ { echo stdout; echo stderr >&2; } >/dev/null 2>&1
$ 
{ echo stdout; echo stderr >&2; } 2>&1 >/dev/null
stderr
Run Code Online (Sandbox Code Playgroud)

并非那些,虽然看起来相似,但不是一回事.

git status 2&>1 > /dev/null实际上,实际上是在git status 2重定向&>1(stdout和stderr文件1)的情况下运行.几乎肯定不是故意的.你的修正几乎肯定是预期的.

$ git init repro
Initialized empty Git repository in /tmp/repro/.git/
$ cd repro/
$ git status
# On branch master
#
# Initial commit
#
nothing to commit
$ ls
$ git status 2>&1
# On branch master
#
# Initial commit
#
nothing to commit
$ ls
$ git status 2&>1
$ ls
1
$ cat 1
# On branch master
#
# Initial commit
#
nothing to commit
Run Code Online (Sandbox Code Playgroud)