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


当前回答

# Close standard output file descriptor
exec 1<&-
# Close standard error file descriptor
exec 2<&-

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

# Redirect standard error to standard output
exec 2>&1

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

现在,一个简单的回显将写入$LOG_FILE,这对于守护进程非常有用。

对于原始帖子的作者,

这取决于你需要实现什么。如果您只需要重定向从脚本调用的命令,那么已经给出了答案。我的任务是在当前脚本中重定向,这会影响到前面提到的代码段之后的所有命令/内置程序(包括fork)。


另一个很酷的解决方案是重定向到标准错误和标准输出,并立即记录到日志文件,这涉及将“一个流”分成两个。此功能由“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(文件描述符#1)和/dev/stderr(文件描述符#2)。实际上,它可以是管道、插座或其他任何东西。

创建文件描述符(FD)#3和#4,并分别指向与#1和#2相同的“位置”。从现在起,更改文件描述符#1不会影响文件描述符#3。现在,文件描述符#3和#4分别指向标准输出和标准错误。这些将用作实际终端标准输出和标准误差。1> >(…)将标准输出重定向到括号中的命令括号(子shell)执行“tee”,读取exec的标准输出(管道),并通过另一个管道重定向到“logger”命令,到达括号中的子shell。同时,它将相同的输入复制到文件描述符#3(终端)第二部分非常类似,是关于对标准错误和文件描述符#2和#4执行相同的技巧。

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

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

…如下:

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

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

如果您想看到更清晰的画面,请在脚本中添加以下两行:

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

其他回答

简短回答:命令>文件名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

它将标准输出和标准错误重定向到文件名。

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

some_command >file.log 2>&1

见第20章。I/O重定向

这种格式比最流行的仅适用于Bash的&>格式更受欢迎。在Bourne shell中,它可以解释为在后台运行命令。格式也更可读-2(标准错误)重定向为1(标准输出)。

“最简单”的方法(仅限Bash 4):

ls * 2>&- 1>&-