Bash sh命令:exec 2>&;1.

Bash sh命令:exec 2>&;1.,bash,shell,exec,Bash,Shell,Exec,这个命令将做什么 exec 2>&1 它将标准误差与标准输出联系起来 2是标准,而1是标准。当您运行一个程序时,您将在stdout中获得正常的输出,但是任何错误或警告通常都会转到stderr。例如,如果您想将所有输出通过管道传输到一个文件,首先将stderr与stdout与2>&1结合起来是很有用的,就像@cma所说的,它将stderr放在stdout上。您可能希望此行为的原因是使用grep或任何其他实用程序来捕获仅出现在stderr上的输出。或者,您可能只想将所有输出(包括s

这个命令将做什么

exec 2>&1 

它将标准误差与标准输出联系起来


2
是标准,而
1
是标准。当您运行一个程序时,您将在stdout中获得正常的输出,但是任何错误或警告通常都会转到stderr。例如,如果您想将所有输出通过管道传输到一个文件,首先将stderr与stdout与
2>&1
结合起来是很有用的,就像@cma所说的,它将stderr放在stdout上。您可能希望此行为的原因是使用grep或任何其他实用程序来捕获仅出现在stderr上的输出。或者,您可能只想将所有输出(包括stderr)保存到一个文件中以供以后处理。

从技术上讲,它将stderr复制或复制到stdout上

通常您不需要exec来执行此操作。exec与文件描述符的一个更典型的用法是指示您要将文件分配给未使用的文件描述符,例如

将起作用,因为它将stdout和stderr都指向文件mydirlist,而命令

ls 2>&1 > mydirlist
只将stdout而不是stderr定向到文件mydirlist,因为stdout重定向到mydirlist之前,stderr是stdout的副本


编辑:这是shell从左到右扫描的工作方式。因此,请阅读第二条,在它说“将stdout发送到mydirlist”之前说“将stderr复制到stdout”。然后将第一个读作“将stdout发送到文件mydirlist”,然后再读“将stderr复制到我设置的stdout”。我知道。这完全不是直觉

我看过的关于“2>&1”的一篇更好的文章是

但目前关于这个问题的答案没有提供的是,为什么你会在一个简单的“执行官”之后这样做。正如exec命令的bash手册页所解释的:“如果未指定命令,则任何重定向都会在当前shell中生效”

我编写了一个名为
out和err.py
的简单脚本,将一行输出写入stdout,另一行写入stderr:

#!/usr/bin/python
import sys
sys.stdout.write('this is stdout.\n')
sys.stderr.write('this is stderr.\n')
然后我将其包装在一个名为out-And-err.sh的shell脚本中,并使用一个“exec 2>&1”:

如果只运行python脚本,则stdout和stderr是分开的:

$ ./out-and-err.py 1> out 2> err
$ cat out
this is stdout.
$ cat err
the is stderr.
但是如果我运行shell脚本,您可以看到exec在以下情况下负责stderr的所有工作:

$ ./out-and-err.sh 1> out 2> err
$ cat out
this is stdout.
this is stderr.
$ cat err
$

如果包装shell脚本不仅仅执行一个python命令,而且需要将所有输出合并到stdout中,则执行“exec2>&1”我遇到的
exec 2>&1
的一个非常有用的应用程序是,当您要为几个用分号分隔的命令合并
stderr
stdout
时。我的特定示例发生在我向PHP中的
popen
发送多个命令时,我希望看到交错的错误,就像您在shell提示符下键入命令时看到的那样:

$ echo hi ; yikes ; echo there
hi
-bash: yikes: command not found
there
$ 
除了最后的
echo
(这是没有意义的,因为
yikes
会导致错误),以下内容不会合并
stderr
stdout

我可以通过以下“硬方法”获得合并输出:

echo hi 2>&1; yikes 2>&1; echo there 2>&1
如果使用
exec
,它看起来更干净,更不容易出错:

exec 2>&1 ; echo hi; echo yikes; echo there

如果执行以分号分隔的三个命令,您可以在终端上看到,
stdout
stderr
的输出被很好地交织在一起。

这些天,我也很讨厌这个问题,但现在我跳出了这个问题。 因此,请允许我解释在CLI中输入
exec 1>&2
后会发生什么

我想把问题一件一件地分解,所以如果你知道已经读过的知识,只需浏览一下就可以节省时间

  • exec
    代表什么:
exec
是Linux中的内置命令。与传统的命令不同,
exec
可以改变当前的shell进程

  • 什么是I/O重定向: 重定向是Linux中的一项功能。使用此选项,您可以更改标准输入/输出设备。在Linux中,默认情况下有三个文件描述符。
    
    句柄名称描述
    0标准输入
    1标准输出
    2标准差
    

  • 让我看一个例子:

  • 打开一个新的终端
  • 获取termianl进程ID
    $pstree-p | grep“term”| tr-d'
  • 检查进程文件描述符。
    $sudo ls-l/proc/{pid}/fd
    bash
    $pstree-p | grep-i“终端”| tr-d“
    ||||-gnome终端-(6008)-bash(7641)-grep(8355)
    $sudo ls-l/proc/7641/fd
    总数0
    lrwx-----1阿洛佩克斯阿洛佩克斯64十月27日19:39 0->/dev/pts/3
    lrwx-----1阿洛佩克斯阿洛佩克斯64十月27日19:39 1->/dev/pts/3
    lrwx-----1阿洛佩克斯阿洛佩克斯64十月27日19:39 2->/dev/pts/3
    lrwx-----1阿洛佩克斯阿洛佩克斯64十月27日19:39 255->/dev/pts/3
    
    如您所见,
    ls
    列出了PID(6860)进程文件。首先,它们都以数字命名(0,1,2),其次它们都将文件链接到/dev/pts/3,这意味着无论什么标准输入/输出/错误都将显示在pts3中
  • 将标准输出更改为/tmp/stdout
    bash
    $ls/tmp/stdout
    ls:无法访问“/tmp/stdout”:没有这样的文件或目录
    $exec 1>/tmp/stdout
    $ls
    $pwd
    $echo“你还好吗”
    $
    
    命令输出在当时消失
  • 打开一个新终端,以检查proc文件
    bash
    $sudo ls-l/proc/7641/fd
    总数0
    lrwx-----1阿洛佩克斯阿洛佩克斯64十月27日19:39 0->/dev/pts/3
    lrwx-----1 alopex alopex 64十月27日19:39 1->/tmp/stdout
    lrwx-----1阿洛佩克斯阿洛佩克斯64十月27日19:39 2->/dev/pts/3
    lrwx-----1阿洛佩克斯阿洛佩克斯64十月27日19:39 255->/dev/pts/3
    
    显然,我们可以注意到1(文件描述符)已经将链接更改为/tmp/stdout。除了我们之外,标准输出传输到/tmp/stdout
  • 恢复重定向。
    bash
    $exec 1>&2
    $cat/tmp/stdout
    a、 嘘
    /家庭/阿洛佩克斯
    你还好吗?
    $sudo ls-l/proc/7641/fd
    总数0
    
    $ echo hi ; yikes ; echo there
    hi
    -bash: yikes: command not found
    there
    $ 
    
    echo hi ; yikes ; echo there 2>&1
    
    echo hi 2>&1; yikes 2>&1; echo there 2>&1
    
    exec 2>&1 ; echo hi; echo yikes; echo there
    
    #! /bin/bash
    
    export LC_ALL=C
    exec 3>&1
    exec 1>log
    exec 2>&1
    set -eux
    
    timestamp () { date +%s.%N; }
    
    loops=${1:-1000}
    t0=$(timestamp)
    for n in $(seq "$loops")
    do
      curl --silent --show-error --noproxy \* http://localhost:8000 &
    done
    wait
    t1=$(timestamp)
    
    printf '%d loops: %0.4f seconds\n' "$loops" "$(bc <<< "$t1 - $t0")" >&3