Bash:多重定向

时间:2015-07-08 18:46:25

标签: linux bash

在脚本的早期,我看到了这一点:

exec 3>&2

后来:

{ $app $conf_file &>$app_log_file & } 1>&3 2>&1

我对此的理解看起来像这样:

  • 创建fd 3
  • 将fd 3输出重定向至stderr
  • (应用执行后)将stdout重定向到fd 3,然后将stderr重定向到stdout

这不是某种循环的疯狂吗? 3> stderr> stdout> 3>等等

我特别关注此行的意图/含义,因为我想使用此脚本valgrind开始运行一些应用程序。我希望看到valgrind的输出穿插了应用程序的日志语句,所以我希望上面的混乱行捕获stderr的默认输出。但是,在导致我想要使用valgrind的一些崩溃中,我看到glibc错误直接输出到终端,而不是在应用程序的日志文件中捕获。

所以,问题:那条执行线究竟做了什么?它是否捕获stderr?如果是这样,为什么当应用程序崩溃时我在命令行上看到glibc输出?如果没有,我应该如何改变它以实现这一目标?

2 个答案:

答案 0 :(得分:4)

您误读了3>&2语法。这意味着打开fd 3并使其成为fd 2的副本。请参阅Duplicating File Descriptors

同样2>&1 意味着使fd 2指向fd 1的位置,这意味着重新打开fd 2作为fd 1的副本(大多数是相同的网络)效果,但不同的语义)。

还要记住,所有重定向都会在它们发生时发生,并且没有"指针"这里。因此2>&1 1>/dev/null 将标准错误重定向到/dev/null它会将标准错误附加到标准输出附加到(可能是终端)的位置。

所以有问题的代码会这样做:

  1. 打开fd 3作为fd 2的副本
  2. 重新打开fd 1作为fd 3的副本
  3. 重新打开fd 2作为fd 1的副本
  4. 实际上,这些行会将所有内容发送到标准错误(或者在初始exec行运行时附加fd 2的位置)。如果重定向是2>&1 1>&3,那么他们就会交换位置。我想知道这是否是该行的初衷,因为正如所写,这是毫无意义的。

    更不用说使用重定向 in 大括号列表,大括号列表外部的重定向是相当无用的。

答案 1 :(得分:2)

好的,让我们看看实践中会发生什么:

peter@tesla:/tmp/test$ bash -c 'exec 3>&2; { sleep 60m &>logfile & } 1>&3 2>&1'  > stdout 2>stderr
peter@tesla:/tmp/test$ psg sleep
peter    22147  0.0  0.0   7232   836 pts/14   S    15:51   0:00 sleep 60m
peter@tesla:/tmp/test$ ll /proc/22147/fd
total 0
lr-x------ 1 peter peter 64 Jul  8 15:51 0 -> /dev/null
l-wx------ 1 peter peter 64 Jul  8 15:51 1 -> /tmp/test/logfile
l-wx------ 1 peter peter 64 Jul  8 15:51 2 -> /tmp/test/logfile
l-wx------ 1 peter peter 64 Jul  8 15:51 3 -> /tmp/test/stderr

我不确定您脚本的作者究竟为何会以这行代码结束。据推测,当他们写作时,这对他们来说是有意义的。大括号外的重定向发生在内部重定向之前,因此它们都被&>logfile覆盖。即使来自bash的错误,如command not found也会在日志文件中结束。

您说当应用崩溃时,您会在终端上看到glibc消息。我认为您的应用在启动后必须使用fd 3。即,它是从为其打开fd 3的脚本开始编写的,否则会打开/dev/tty或其他内容。

BTW,psg是我在.bashrc中定义的函数:
psg(){ ps aux | grep "${@:-$USER}" | grep -v grep; }
最近更新为:

psg(){  local pids=$(pgrep -f "${@:--u$USER}"); [[ $pids ]] && ps u   -p $pids; }
psgw(){ local pids=$(pgrep -f "${@:--u$USER}"); [[ $pids ]] && ps uww -p $pids; }