在 Bash 中重定向 stderr 和 stdout

发布于 2024-07-15 02:51:36 字数 261 浏览 9 评论 0 原文

我想重定向 标准输出单个文件进程的标准错误。 我如何在 Bash 中做到这一点?

I want to redirect both standard output and standard error of a process to a single file. How do I do that in Bash?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

若言繁花未落 2024-07-22 02:51:36

请查看此处。 它应该是:

yourcommand &> filename

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

Take a look here. It should be:

yourcommand &> filename

It redirects both standard output and standard error to file filename.

囚我心虐我身 2024-07-22 02:51:36
do_something 2>&1 | tee -a some_file

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

do_something 2>&1 | tee -a some_file

This is going to redirect standard error to standard output and standard output to some_file and print it to standard output.

在风中等你 2024-07-22 02:51:36

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

some_command >file.log 2>&1

注意:重定向的顺序很重要
以下等效。

some_command 2>&1 >file.log

请参阅第 20 章 I/O 重定向

这种格式优于仅适用于 Bash 的最流行的 > 格式。 在 Bourne shell 中,它可以被解释为在后台运行命令。 此外,格式更具可读性 - 2(标准错误)重定向到 1(标准输出)。

You can redirect stderr to stdout and the stdout into a file:

some_command >file.log 2>&1

NB: The order of redirection is significant.
The following is not equivalent.

some_command 2>&1 >file.log

See Chapter 20. I/O Redirection

This format is preferred over the most popular &> format that only works in Bash. In Bourne shell it could be interpreted as running the command in background. Also the format is more readable - 2 (is standard error) redirected to 1 (standard output).

水中月 2024-07-22 02:51:36
# 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'"

现在,一个简单的 echo 将写入 $LOG_FILE,这对于守护进程很有用。

对于原始帖子的作者,

这取决于您需要实现的目标。 如果您只需要重定向输入/输出从脚本调用的命令,答案已经给出。 我的任务是在当前脚本内重定向,该脚本会影响上述代码片段之后的所有命令/内置命令(包括分支)。


另一个很酷的解决方案是重定向到标准错误和标准输出并立即记录到日志文件,这涉及将“流”分成两部分。 此功能由“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
# 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'"

Now, a simple echo will write to $LOG_FILE, and it is useful for daemonizing.

To the author of the original post,

It depends what you need to achieve. If you just need to redirect in/out of a command you call from your script, the answers are already given. Mine is about redirecting within current script which affects all commands/built-ins (includes forks) after the mentioned code snippet.


Another cool solution is about redirecting to both standard error and standard output and to log to a log file at once which involves splitting "a stream" into two. This functionality is provided by 'tee' command which can write/append to several file descriptors (files, sockets, pipes, etc.) at once: 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
}

So, from the beginning. Let's assume we have a terminal connected to /dev/stdout (file descriptor #1) and /dev/stderr (file descriptor #2). In practice, it could be a pipe, socket or whatever.

  • Create file descriptors (FDs) #3 and #4 and point to the same "location" as #1 and #2 respectively. Changing file descriptor #1 doesn't affect file descriptor #3 from now on. Now, file descriptors #3 and #4 point to standard output and standard error respectively. These will be used as real terminal standard output and standard error.
  • 1> >(...) redirects standard output to command in parentheses
  • Parentheses (sub-shell) executes 'tee', reading from exec's standard output (pipe) and redirects to the 'logger' command via another pipe to the sub-shell in parentheses. At the same time it copies the same input to file descriptor #3 (the terminal)
  • the second part, very similar, is about doing the same trick for standard error and file descriptors #2 and #4.

The result of running a script having the above line and additionally this one:

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

...is as follows:

$ ./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

If you want to see clearer picture, add these two lines to the script:

ls -l /proc/self/fd/
ps xf
听你说爱我 2024-07-22 02:51:36
bash your_script.sh 1>file.log 2>&1

1>file.log 指示 shell 将标准输出发送到文件 file.log2>&1 告诉它重定向标准错误(文件描述符 2)到标准输出(文件描述符 1)。

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

bash your_script.sh 1>file.log 2>&1

1>file.log instructs the shell to send standard output to the file file.log, and 2>&1 tells it to redirect standard error (file descriptor 2) to standard output (file descriptor 1).

Note: The order matters as liw.fi pointed out, 2>&1 1>file.log doesn't work.

转身以后 2024-07-22 02:51:36

奇怪的是,这有效:

yourcommand &> filename

但这会产生语法错误:

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

您必须使用:

yourcommand 1>> filename 2>&1

Curiously, this works:

yourcommand &> filename

But this gives a syntax error:

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

You have to use:

yourcommand 1>> filename 2>&1
仙女 2024-07-22 02:51:36

简短回答: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 而不是标准输出。 完成此操作后,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-> 标准输出。 如果是这样,由于将 1 重定向到 /dev/null,代码 2>&1 >/dev/null 将给出指针链 2 -> 。 1-> /dev/null,因此代码不会生成任何内容,与我们上面看到的相反。


最后,我要指出的是,有一种更简单的方法可以做到这一点:

来自第 3.6.4 节 这里,我们看到我们可以使用运算符&>来重定向stdout和stderr。 因此,要将任何命令的 stderr 和 stdout 输出重定向到 \dev\null (这会删除输出),我们只需键入
$ 命令 &> /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。 一个产生输出,另一个不产生输出!

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

有关重定向的 Bash 文档 , 文件描述符表说明, 指针简介

Short answer: Command >filename 2>&1 or Command &>filename


Explanation:

Consider the following code which prints the word "stdout" to stdout and the word "stderror" to stderror.

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

Note that the '&' operator tells bash that 2 is a file descriptor (which points to the stderr) and not a file name. If we left out the '&', this command would print stdout to stdout, and create a file named "2" and write stderror there.

By experimenting with the code above, you can see for yourself exactly how redirection operators work. For instance, by changing which file which of the two descriptors 1,2, is redirected to /dev/null the following two lines of code delete everything from the stdout, and everything from stderror respectively (printing what remains).

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

Now, we can explain why the solution why the following code produces no output:

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

To truly understand this, I highly recommend you read this webpage on file descriptor tables. Assuming you have done that reading, we can proceed. Note that Bash processes left to right; thus Bash sees >/dev/null first (which is the same as 1>/dev/null), and sets the file descriptor 1 to point to /dev/null instead of the stdout. Having done this, Bash then moves rightwards and sees 2>&1. This sets the file descriptor 2 to point to the same file as file descriptor 1 (and not to file descriptor 1 itself!!!! (see this resource on pointers for more info)) . Since file descriptor 1 points to /dev/null, and file descriptor 2 points to the same file as file descriptor 1, file descriptor 2 now also points to /dev/null. Thus both file descriptors point to /dev/null, and this is why no output is rendered.


To test if you really understand the concept, try to guess the output when we switch the redirection order:

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

stderror

The reasoning here is that evaluating from left to right, Bash sees 2>&1, and thus sets the file descriptor 2 to point to the same place as file descriptor 1, ie stdout. It then sets file descriptor 1 (remember that >/dev/null = 1>/dev/null) to point to >/dev/null, thus deleting everything which would usually be send to to the standard out. Thus all we are left with was that which was not send to stdout in the subshell (the code in the parentheses)- i.e. "stderror".
The interesting thing to note there is that even though 1 is just a pointer to the stdout, redirecting pointer 2 to 1 via 2>&1 does NOT form a chain of pointers 2 -> 1 -> stdout. If it did, as a result of redirecting 1 to /dev/null, the code 2>&1 >/dev/null would give the pointer chain 2 -> 1 -> /dev/null, and thus the code would generate nothing, in contrast to what we saw above.


Finally, I'd note that there is a simpler way to do this:

From section 3.6.4 here, we see that we can use the operator &> to redirect both stdout and stderr. Thus, to redirect both the stderr and stdout output of any command to \dev\null (which deletes the output), we simply type
$ command &> /dev/null
or in case of my example:

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

Key takeaways:

  • File descriptors behave like pointers (although file descriptors are not the same as file pointers)
  • Redirecting a file descriptor "a" to a file descriptor "b" which points to file "f", causes file descriptor "a" to point to the same place as file descriptor b - file "f". It DOES NOT form a chain of pointers a -> b -> f
  • Because of the above, order matters, 2>&1 >/dev/null is != >/dev/null 2>&1. One generates output and the other does not!

Finally have a look at these great resources:

Bash Documentation on Redirection, An Explanation of File Descriptor Tables, Introduction to Pointers

迷你仙 2024-07-22 02:51:36
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 ;(

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

It is related: Writing standard output and standard error to syslog.

It almost works, but not from xinetd ;(

小伙你站住 2024-07-22 02:51:36

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

例如:

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

或者

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”)。

For the situation when "piping" is necessary, you can use |&.

For example:

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

These Bash-based solutions can pipe standard output and standard error separately (from standard error of "sort -c", or from standard error to "sort -h").

二智少女猫性小仙女 2024-07-22 02:51:36

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

这是我找到的解决方案:

command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
  • 首先交换 stderr 和 stdout,
  • 然后将 stdout 附加到日志文件
  • 管道 stderr to tee 并将其也附加到日志文件

I wanted a solution to have the output from stdout plus stderr written into a log file and stderr still on console. So I needed to duplicate the stderr output via tee.

This is the solution I found:

command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
  • First swap stderr and stdout
  • then append the stdout to the log file
  • pipe stderr to tee and append it also to the log file
定格我的天空 2024-07-22 02:51:36

添加到 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"

Adding to what Fernando Fabreti did, I changed the functions slightly and removed the &- closing and it worked for me.

    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"
想挽留 2024-07-22 02:51:36

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

ls * 2>&- 1>&-

The "easiest" way (Bash 4 only):

ls * 2>&- 1>&-
腹黑女流氓 2024-07-22 02:51:36

当您考虑使用诸如 exec 2>&1 之类的东西时,我发现如果可能的话,使用 Bash 函数重写代码会更容易阅读,如下所示:

function myfunc(){
  [...]
}

myfunc &>mylog.log

In situations when you consider using things like exec 2>&1, I find it easier to read, if possible, rewriting code using Bash functions like this:

function myfunc(){
  [...]
}

myfunc &>mylog.log
智商已欠费 2024-07-22 02:51:36

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

The following functions can be used to automate the process of toggling outputs beetwen stdout/stderr and a logfile.

#!/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"
    }

Example of usage inside script:

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"
丶情人眼里出诗心の 2024-07-22 02:51:36

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

command >& file

如果使用 command &> ; file,它将给出“无效的空命令”错误。

For tcsh, I have to use the following command:

command >& file

If using command &> file, it will give an "Invalid null command" error.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文