我想将流程的标准输出和标准错误重定向到单个文件。我在巴什怎么做?


当前回答

奇怪的是,这是有效的:

yourcommand &> filename

但这会产生语法错误:

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

您必须使用:

yourcommand 1>> filename 2>&1

其他回答

do_something 2>&1 | tee -a some_file

这会将标准错误重定向到标准输出,将标准输出重定向到some_file并将其打印到标准输出。

简短回答:命令>文件名2>&1或命令>>文件名


说明:

考虑以下代码,该代码将单词“stdout”打印到stdout,将单词“tderror”打印到tderror。

$ (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

标准错误

这里的理由是,从左到右计算,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-file“f”相同的位置。它不会形成指针链a->b->f由于以上原因,顺序很重要,2>&1>/dev/null是!=>/dev/null 2>&1。一个生成输出,另一个不生成!


最后看看这些伟大的资源:

重定向的Bash文档,文件描述符表的解释,指针介绍

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" )

它是相关的:将标准输出和标准错误写入syslog。

它几乎可以工作,但不能从xinetd;(

奇怪的是,这是有效的:

yourcommand &> filename

但这会产生语法错误:

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

您必须使用:

yourcommand 1>> filename 2>&1

以下函数可用于自动切换stdout/stderr和日志文件之间的输出。

#!/bin/bash

    #set -x

    # global vars
    OUTPUTS_REDIRECTED="false"
    LOGFILE=/dev/stdout

    # "private" function used by redirect_outputs_to_logfile()
    function save_standard_outputs {
        if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
            exit 1;
        fi
        exec 3>&1
        exec 4>&2

        trap restore_standard_outputs EXIT
    }

    # Params: $1 => logfile to write to
    function redirect_outputs_to_logfile {
        if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
            exit 1;
        fi
        LOGFILE=$1
        if [ -z "$LOGFILE" ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"

        fi
        if [ ! -f $LOGFILE ]; then
            touch $LOGFILE
        fi
        if [ ! -f $LOGFILE ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
            exit 1
        fi

        save_standard_outputs

        exec 1>>${LOGFILE%.log}.log
        exec 2>&1
        OUTPUTS_REDIRECTED="true"
    }

    # "private" function used by save_standard_outputs() 
    function restore_standard_outputs {
        if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: Cannot restore standard outputs because they have NOT been redirected"
            exit 1;
        fi
        exec 1>&-   #closes FD 1 (logfile)
        exec 2>&-   #closes FD 2 (logfile)
        exec 2>&4   #restore stderr
        exec 1>&3   #restore stdout

        OUTPUTS_REDIRECTED="false"
    }

脚本内部用法示例:

echo "this goes to stdout"
redirect_outputs_to_logfile /tmp/one.log
echo "this goes to logfile"
restore_standard_outputs 
echo "this goes to stdout"