当前位置:  开发笔记 > 编程语言 > 正文

在Bash中重定向stderr和stdout

如何解决《在Bash中重定向stderr和stdout》经验,为你挑选了9个好方法。

我想将进程的stdout和stderr重定向到单个文件.我怎么用Bash做到这一点?



1> dirkgently..:

看看这里.应该:

yourcommand &>filename

(重定向两者stdoutstderr文件名).


根据[Bash Hackers Wiki](http://wiki.bash-hackers.org/syntax/redirection)不推荐使用此语法.是吗?
一个额外的提示:如果你在脚本中使用它,请确保它以`#!/ bin/bash`而不是`#!/ bin/sh`开头,因为in需要bash.
据http://wiki.bash-hackers.org/scripting/obsolete,这似乎是在这个意义上,它不是POSIX的一部分已经过时,但在bash手册页使得从庆典被删除在没有它的提不远的将来.手册页确实指定'&>'优先于'>&',否则等效.
我想我们不应该使用&>因为它不在POSIX中,并且常见的shell如"dash"不支持它.
或者&>>来追加而不是覆盖.

2> Marko..:
do_something 2>&1 | tee -a some_file

这将把stderr重定向到stdout和stdout some_file 并将其打印到stdout.


在AIX(ksh)上,您的解决方案可行.接受的答案`do_something&> filename`没有.+1.
@Daniel,但这个问题是关于bash的
注意(默认情况下)这有副作用,`$?`不再引用`do_something`的退出状态,而是'tee`的退出状态.
我得到`模糊的输出重定向.任何想法为什么?

3> f3lix..:

您可以将stderr重定向到stdout,将stdout重定向到文件中:

some_command >file.log 2>&1 

请参见http://tldp.org/LDP/abs/html/io-redirection.html

此格式比仅适用于bash的最流行的&>格式更受欢迎.在Bourne shell中,它可以解释为在后台运行命令.此格式更易读2(STDERR)重定向到1(STDOUT).

编辑:改变了评论中指出的顺序


这会将stderr重定向到原始stdout,而不是重定向到stdout所在的文件.在'> file.log'之后放'2>&1'就可以了.
唉,对不起,回声"foo"1 >> bar.txt 2>&1
我认为2>&1将stderr重定向到stdout的解释是错误的; 我相信它更准确地说它将stderr发送到stdout此时正在发生的同一个地方.因此,在第一次重定向之后放置2>&1*是必不可少的.
如果你想附加到一个文件,那么你必须这样做:echo"foo"2>&1 1 >> bar.txt AFAIK没有办法使用&>追加

4> quizac..:
# Close STDOUT file descriptor
exec 1<&-
# Close STDERR FD
exec 2<&-

# Open STDOUT as $LOG_FILE file for read and write.
exec 1<>$LOG_FILE

# Redirect STDERR to STDOUT
exec 2>&1

echo "This line will appear in $LOG_FILE, not 'on screen'"

现在,简单的echo将写入$ LOG_FILE.适用于守护进程.

致原帖的作者,

这取决于你需要实现的目标.如果您只需要重定向进出您从脚本调用的命令,则已经给出了答案.我的目标是当前脚本中重定向,这会在所提到的代码片段之后影响所有命令/内置函数(包括forks).


另一个很酷的解决方案是立即重定向到std-err/out和记录器或日志文件,这涉及将"流"拆分为两个.此功能由'tee'命令提供,该命令可以一次写入/附加到多个文件描述符(文件,套接字,管道等):tee FILE1 FILE2 ...>(cmd1)>(cmd2)...

exec 3>&1 4>&2 1> >(tee >(logger -i -t 'my_script_tag') >&3) 2> >(tee >(logger -i -t 'my_script_tag') >&4)
trap 'cleanup' INT QUIT TERM EXIT


get_pids_of_ppid() {
    local ppid="$1"

    RETVAL=''
    local pids=`ps x -o pid,ppid | awk "\\$2 == \\"$ppid\\" { print \\$1 }"`
    RETVAL="$pids"
}


# Needed to kill processes running in background
cleanup() {
    local current_pid element
    local pids=( "$$" )

    running_pids=("${pids[@]}")

    while :; do
        current_pid="${running_pids[0]}"
        [ -z "$current_pid" ] && break

        running_pids=("${running_pids[@]:1}")
        get_pids_of_ppid $current_pid
        local new_pids="$RETVAL"
        [ -z "$new_pids" ] && continue

        for element in $new_pids; do
            running_pids+=("$element")
            pids=("$element" "${pids[@]}")
        done
    done

    kill ${pids[@]} 2>/dev/null
}

所以,从一开始.假设我们的终端连接到/ dev/stdout(FD#1)和/ dev/stderr(FD#2).在实践中,它可能是管道,插座或其他什么.

创建FD#3和#4并分别指向与#1和#2相同的"位置".从现在开始,改变FD#1不会影响FD#3.现在,FD#3和#4分别指向STDOUT和STDERR.这些将用作实际终端STDOUT和STDERR.

1 >>(...)将STDOUT重定向到parens中的命令

parens(sub-shell)从exec的STDOUT(管道)执行'tee'读取,并通过另一个管道重定向到'logger'命令到parens中的子shell.同时将相同的输入复制到FD#3(终端)

第二部分,非常相似,是关于为STDERR和FD#2和#4做同样的技巧.

运行具有上述行和另外一行的脚本的结果:

echo "Will end up in STDOUT(terminal) and /var/log/messages"

......如下:

$ ./my_script
Will end up in STDOUT(terminal) and /var/log/messages

$ tail -n1 /var/log/messages
Sep 23 15:54:03 wks056 my_script_tag[11644]: Will end up in STDOUT(terminal) and /var/log/messages

如果要查看更清晰的图片,请将以下两行添加到脚本中:

ls -l /proc/self/fd/
ps xf


这是真的,虽然它取决于意图.我的方法是始终创建一个唯一的带时间戳的日志文件.另一个是追加.两种方式都是"可以转换"的.我更喜欢需要较少解析的单独文件,但正如我所说,无论是什么让你的船漂浮:)

5> Guðmundur H..:
bash your_script.sh 1>file.log 2>&1

1>file.log指示shell将STDOUT发送到文件file.log,并2>&1告诉它将STDERR(文件描述符2)重定向到STDOUT(文件描述符1).

注意:顺序很重要liw.fi指出,2>&1 1>file.log不起作用.



6> 小智..:

奇怪的是,这有效:

yourcommand &> filename

但这会产生语法错误:

yourcommand &>> filename
syntax error near unexpected token `>'

你必须使用:

yourcommand 1>> filename 2>&1


`&>>`似乎适用于BASH 4:`$ echo $ BASH_VERSION 4.1.5(1)-release $(echo to stdout; echo to stderr>/dev/stderr)&>>/dev/null`

7> Evan Rosica..:

简短回答:Command >filename 2>&1Command &>filename


说明:

考虑下面的代码,它将单词"stdout"打印到stdout,将单词"stderror"输出到stderror.

$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror

请注意,'&'运算符告诉bash 2是文件描述符(指向stderr)而不是文件名.如果我们省略'&',这个命令将打印stdout到stdout,并创建一个名为"2"的文件并写stderror在那里.

通过试验上面的代码,您可以准确地了解重定向运算符的工作原理.例如,通过将哪两个描述符中的哪个文件1,2重定向到/dev/null以下两行代码,从stdout中删除所有内容,从stderror中删除所有内容(打印剩余的内容).

$ (echo "stdout"; echo "stderror" >&2) 1>/dev/null
stderror
$ (echo "stdout"; echo "stderror" >&2) 2>/dev/null
stdout

现在,我们可以解释为什么以下代码不产生输出的原因:

(echo "stdout"; echo "stderror" >&2) >/dev/null 2>&1

要真正理解这一点,我强烈建议您在文件描述符表上阅读此网页.假设你已经完成了阅读,我们就可以继续了.请注意Bash从左到右处理; 因此Bash >/dev/null首先看到(与之相同1>/dev/null),并将文件描述符1设置为指向/ dev/null而不是stdout.完成此操作后,Bash向右移动并看到2>&1.这将文件描述符2设置为指向与文件描述符1 相同的文件(而不是文件描述符1本身!!!!(有关更多信息,请参阅指针上的此资源)).由于文件描述符1指向/ dev/null,并且文件描述符2指向与文件描述符1相同的文件,因此文件描述符2现在也指向/ dev/null.因此,两个文件描述符都指向/ dev/null,这就是没有输出的原因.


要测试您是否真的理解这个概念,请在切换重定向顺序时尝试猜测输出:

(echo "stdout"; echo "stderror" >&2)  2>&1 >/dev/null

stderror

这里的推理是从左到右进行评估,Bash看到2>&1,因此将文件描述符2设置为指向与文件描述符1相同的位置,即stdout.然后它设置文件描述符1(记住>/dev/null = 1>/dev/null)指向>/dev/null,从而删除通常发送到标准输出的所有内容.因此我们剩下的就是那些没有发送到子shell中的stdout(括号中的代码) - 即"stderror".值得注意的是,即使1只是指向stdout的指针,将指针2重定向到1 2>&1也不会形成指针链2 - > 1 - > stdout.如果确实如此,由于将1重定向到/ dev/null,代码2>&1 >/dev/null会给指针链2 - > 1 - >/dev/null,因此代码不产生任何东西,与我们上面看到的相反.


最后,我注意到有一种更简单的方法可以做到这一点:

从这里的 3.6.4节,我们看到我们可以使用运算符&>重定向stdout和stderr.因此,要将任何命令的stderr和stdout输出重定向到\dev\null(删除输出),我们只需键入 $ command &> /dev/null 或在我的示例中:

$ (echo "stdout"; echo "stderror" >&2) &>/dev/null

关键要点:

文件描述符的行为类似于指针(尽管文件描述符与文件指针不同)

将文件描述符"a"重定向到指向文件"f"的文件描述符"b",使文件描述符"a"指向与文件描述符b-文件"f"相同的位置.它不会形成一个指针链a - > b - > f

由于上述原因,订单很重要,2>&1 >/dev/null是!= >/dev/null 2>&1.一个产生输出而另一个不产生!


最后看看这些很棒的资源:

对重定向猛砸文档,文件描述表的说明,介绍指针



8> 小智..:
LOG_FACILITY="local7.notice"
LOG_TOPIC="my-prog-name"
LOG_TOPIC_OUT="$LOG_TOPIC-out[$$]"
LOG_TOPIC_ERR="$LOG_TOPIC-err[$$]"

exec 3>&1 > >(tee -a /dev/fd/3 | logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_OUT" )
exec 2> >(logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_ERR" )

它是相关的:将stdOut和stderr写入syslog.

它几乎可以工作,但不是来自xinted;(



9> bebbo..:

我想要一个解决方案,将stdout和stderr的输出写入日志文件,stderr仍然在控制台上.所以我需要通过tee复制stderr输出.

这是我找到的解决方案:

command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile

首先交换stderr和stdout

然后将stdout附加到日志文件中

pipe stderr to tee并将其附加到日志文件中

推荐阅读
拾味湖
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有