这个命令会做什么?
exec 2>&1
答案 0 :(得分:36)
从技术上讲,它将stderr复制或复制到stdout上。
通常你不需要exec来执行此操作。 exec与文件描述符的更典型用法是指示您要将文件分配给未使用的文件描述符,例如
exec 35< my_input
BTW不要忘记管道到文件时的声明顺序很重要,所以
ls > mydirlist 2>&1
将起作用,因为它将stdout和stderr都指向文件mydirlist,而命令
ls 2>&1 > mydirlist
只将stdout,而不是stderr指向文件mydirlist,因为在stdout被重定向到mydirlist之前stderr被制作了stdout的副本。
编辑:这是shell从左到右扫描的方式。所以在第二个说“将stdout发送到stdout”之前,请先阅读“将stdout发送到mydirlist”。然后读第一个说“将stdout发送到文件mydirlist”然后它说“将stderr复制到我设置的stdout”。我知道。这完全不直观!
答案 1 :(得分:31)
我在“2&gt;&amp; 1”上看到的更好的文章之一是Bash One-Liners Explained, Part III: All about redirections。
但目前这个问题的答案未能提供的是为什么你想在一个普通的“执行”之后做这个。正如exec命令的bash手册页所述:“如果未指定命令,则任何重定向都会在当前shell中生效”。
我写了一个名为out-and-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&gt;&amp; 1”:
#!/bin/bash
exec 2>&1
./out-and-err.py
如果我只运行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,那么执行“exec 2&gt;&amp; 1”将使你很容易。
答案 2 :(得分:13)
它将标准错误与标准输出联系起来
2
是stderr而1
是标准输出。当您运行程序时,您将在stdout中获得正常输出,但任何错误或警告通常都会发送到stderr。例如,如果要将所有输出传递给文件,首先将stderr与stdout与2>&1
组合在一起很有用。
答案 3 :(得分:2)
就像@cma所说,它将stderr放在stdout上。您可能希望此行为的原因是使用grep或任何其他实用程序来捕获仅出现在stderr上的输出。或者您可能只想将所有输出(包括stderr)保存到文件中以供以后处理。
答案 4 :(得分:0)
我遇到的exec 2>&1
的一个非常有用的应用是当你想要将stderr
和stdout
合并为由分号分隔的几个命令时。我的特定示例发生在我向PHP中的popen
发送多个命令时,我希望看到错误交错,就像您在shell提示符下键入命令一样:
$ echo hi ; yikes ; echo there
hi
-bash: yikes: command not found
there
$
以下不合并stderr
和stdout
,但最后echo
除外(因为yikes
导致错误,这是毫无意义的):
echo hi ; yikes ; echo there 2>&1
我可以通过以下方式获得合并输出“硬路径”:
echo hi 2>&1; yikes 2>&1; echo there 2>&1
如果您使用exec
:
exec 2>&1 ; echo hi; echo yikes; echo there
如果你执行了以分号分隔的三个命令,那么就会像在终端上看到的那样完全交错stdout
和stderr
输出。
答案 5 :(得分:0)
这些天,我也很困惑这个问题,但是现在我从中脱身了。
因此,请允许我解释一下在CLI中输入exec 1>&2
后发生的情况。
我想逐一销毁问题,因此,如果您知道已经知道的知识,只需略读即可以节省时间。
exec
代表什么: exec
是Linux中的内置命令。与仅叉一个子shell进程的传统命令不同,exec
可以更改当前的shell进程。
什么是I / O重定向:
重定向是Linux中的一项功能。这样,您可以更改标准输入/输出设备。在Linux中,默认情况下有三个文件描述符。
Handle Name Description
0 stdin Standard input
1 stdout Standard output
2 stderr Standard error
让我看一个例子:
$ pstree -p | grep 'term' | tr -d ' '
$ sudo ls -l /proc/{pid}/fd
bash
$ pstree -p | grep -i 'terminal' | tr -d ' '
||||-gnome-terminal-(6008)-+-bash(7641)-+-grep(8355)
$ sudo ls -l /proc/7641/fd
total 0
lrwx------ 1 alopex alopex 64 Oct 27 19:39 0 -> /dev/pts/3
lrwx------ 1 alopex alopex 64 Oct 27 19:39 1 -> /dev/pts/3
lrwx------ 1 alopex alopex 64 Oct 27 19:39 2 -> /dev/pts/3
lrwx------ 1 alopex alopex 64 Oct 27 19:39 255 -> /dev/pts/3
如您所见,ls
列出了PID(6860)过程文件。首先,它们都以数字(0、1、2)命名,其次,所有链接文件都链接到/ dev / pts / 3,这意味着所有标准输入/输出/错误都将显示在pts3中。bash
$ ls /tmp/stdout
ls: cannot access '/tmp/stdout': No such file or directory
$ exec 1>/tmp/stdout
$ ls
$ pwd
$ echo "Are you ok"
$
那时命令输出消失了。bash
$ sudo ls -l /proc/7641/fd
total 0
lrwx------ 1 alopex alopex 64 Oct 27 19:39 0 -> /dev/pts/3
lrwx------ 1 alopex alopex 64 Oct 27 19:39 1 -> /tmp/stdout
lrwx------ 1 alopex alopex 64 Oct 27 19:39 2 -> /dev/pts/3
lrwx------ 1 alopex alopex 64 Oct 27 19:39 255 -> /dev/pts/3
显然,我们可以注意到1(文件描述符)已经更改了/ tmp / stdout的链接。和我们一样,标准输出传输到/ tmp / stdout bash
$ exec 1>&2
$ cat /tmp/stdout
a.sh
/home/alopex
Are you ok
$ sudo ls -l /proc/7641/fd
total 0
lrwx------ 1 alopex alopex 64 Oct 27 19:39 0 -> /dev/pts/3
lrwx------ 1 alopex alopex 64 Oct 27 19:39 1 -> /dev/pts/3
lrwx------ 1 alopex alopex 64 Oct 27 19:39 2 -> /dev/pts/3
lrwx------ 1 alopex alopex 64 Oct 27 19:39 255 -> /dev/pts/3
同样,将1(文件描述符)链接到/ dev / pts / 3,我们可以再次看到输出。摘要:
exec 1>&2
进行标准输出--->标准错误答案 6 :(得分:0)
实际示例,何时使用 exec 2>&1
:以下示例是执行 1000 个 HTTP 请求并测量时间的测试用例。测试用例的输出应该发送到日志文件,但测量的时间应该发送到标准输出。
为了达到这个标准输出被复制:exec 3>&1
。然后标准输出被重定向到文件 log
: exec 1>log
。最后标准错误被重定向到标准输出:exec 2>&1
。这意味着标准错误也将发送到文件 log
,因为标准输出已经被重定向。在此文件描述符之后 3 仍可用于向脚本的标准输出发送消息,尽管其他所有内容都进入日志文件:printf ... >&3
。
#! /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