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


当前回答

do_something 2>&1 | tee -a some_file

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

其他回答

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

ls * 2>&- 1>&-
# 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

以下函数可用于自动切换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"

对于tcsh,我必须使用以下命令:

command >& file

如果使用命令&>文件,它将给出“无效空命令”错误。

对于需要“管道”的情况,可以使用|&。

例如:

echo -ne "15\n100\n" | sort -c |& tee >sort_result.txt

or

TIMEFORMAT=%R;for i in `seq 1 20` ; do time kubectl get pods | grep node >>js.log ; done |& sort -h

这些基于Bash的解决方案可以分别处理标准输出和标准错误(从标准错误“sort-c”,或从标准错误到“sort-h”)。