在Bash中重定向stderr和stdout

863

4
我想说这是一个令人惊讶的有用问题。很多人不知道如何做到这一点,因为他们不需要经常这样做,而且这也不是Bash最好记录的行为。 - Robert Wm Ruedisueli
4
有时候看到输出结果(通常情况下)并将其重定向到一个文件中也很有用。请参考下面Marko的答案。(我在这里说这句话是因为如果第一个被接受的答案足以解决问题,那么只看第一个答案很容易忽略其他提供有用信息的答案。) - jvriesem
15个回答

2

最简单的方法(仅适用于Bash 4):

ls * 2>&- 1>&-

2

Fernando Fabreti 所做的工作基础上,我稍微修改了一下函数并移除了 &- ,这对我有用。

    function saveStandardOutputs {
      if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
        exec 3>&1
        exec 4>&2
        trap restoreStandardOutputs EXIT
      else
          echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
          exit 1;
      fi
  }

  # Parameters: $1 => logfile to write to
  function redirectOutputsToLogfile {
      if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
        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
        saveStandardOutputs
        exec 1>>${LOGFILE}
        exec 2>&1
        OUTPUTS_REDIRECTED="true"
      else
        echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
          exit 1;
      fi
  }

  function restoreStandardOutputs {
      if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
      exec 1>&3   #restore stdout
      exec 2>&4   #restore stderr
      OUTPUTS_REDIRECTED="false"
     fi
  }

  LOGFILE_NAME="tmp/one.log"
  OUTPUTS_REDIRECTED="false"

  echo "this goes to standard output"
  redirectOutputsToLogfile $LOGFILE_NAME
  echo "this goes to logfile"
  echo "${LOGFILE_NAME}"
  restoreStandardOutputs
  echo "After restore this goes to standard output"

2
在考虑使用诸如 exec 2>&1 这样的东西时,如果可能的话,重写代码并使用 Bash 函数,我发现阅读起来更容易:

function myfunc(){
  [...]
}

myfunc &>mylog.log

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"

当我使用你的函数并尝试恢复标准输出时,会出现回显:写入错误:坏文件号。重定向完美运行...但恢复似乎不起作用。 - Thom Schumacher
为了让你的脚本在我的电脑上运行,我不得不注释掉这些行并改变顺序: #exec 1>&- #关闭FD 1(日志文件) #exec 2>&- #关闭FD 2(日志文件); exec 1>&3 #恢复标准输出 exec 2>&4 #恢复标准错误输出 - Thom Schumacher
很抱歉听到这个消息。在CentOS 7,bash 4.2.46中运行时我没有收到任何错误信息。 我已经注释了我得到这些命令的参考文献。它是:Ref: http://logan.tw/posts/2016/02/20/open-and-close-files-in-bash/ - Fernando Fabreti
我在 AIX 上运行这些命令,可能就是这个原因。我添加了一个帖子来说明我所做的修复。 - Thom Schumacher

0

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

command >& file

如果使用command &> file,会出现“无效的空命令”错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接