在 Bash 中重定向 stderr 和 stdout

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/637827/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 18:01:07  来源:igfitidea点击:

Redirect stderr and stdout in Bash

bashshellredirectpipe

提问by flybywire

I want to redirect both stdout and stderr of a process to a single file. How do I do that in Bash?

我想将进程的 stdout 和 stderr 都重定向到单个文件。我如何在 Bash 中做到这一点?

回答by dirkgently

Take a look here. Should be:

看看这里。应该:

yourcommand &>filename

(redirects both stdoutand stderrto filename).

(将stdout和都重定向stderr到文件名)。

回答by Marko

do_something 2>&1 | tee -a some_file

This is going to redirect stderr to stdout and stdout to some_fileandprint it to stdout.

这会将 stderr 重定向到 stdout 并将 stdout 重定向到some_file并将其打印到 stdout。

回答by f3lix

You can redirect stderrto stdoutand the stdoutinto a file:

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

some_command >file.log 2>&1 

See http://tldp.org/LDP/abs/html/io-redirection.html

http://tldp.org/LDP/abs/html/io-redirection.html

This format is preferred than the most popular &> format that only work in bash. In Bourne shell it could be interpreted as running the command in background. Also the format is more readable 2 (is STDERR) redirected to 1 (STDOUT).

与仅在 bash 中工作的最流行的 &> 格式相比,这种格式更受欢迎。在 Bourne shell 中,它可以解释为在后台运行命令。格式也更具可读性 2(是 STDERR)重定向到 1(STDOUT)。

EDIT: changed the order as pointed out in the comments

编辑:更改了评论中指出的顺序

回答by quizac

# Close STDOUT file descriptor
exec 1<&-
# Close STDERR FD
exec 2<&-

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

# Redirect STDERR to STDOUT
exec 2>&1

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

Now, simple echo will write to $LOG_FILE. Useful for daemonizing.

现在,简单的 echo 将写入 $LOG_FILE。用于守护进程。

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 withincurrent script which affects all commands/built-ins(includes forks) after the mentioned code snippet.

这取决于您需要达到的目标。如果您只需要重定向进/出您从脚本调用的命令,那么答案已经给出。我的是当前脚本中重定向它会在提到的代码片段之后影响所有命令/内置插件(包括 fork)。



Another cool solution is about redirecting to both std-err/out AND to logger or 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) ...

另一个很酷的解决方案是同时重定向到 std-err/out 和记录器或日志文件,这涉及将“流”一分为二。此功能由“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=""

    RETVAL=''
    local pids=`ps x -o pid,ppid | awk "\ == \"$ppid\" { print \ }"`
    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 terminal connected to /dev/stdout(FD #1) and /dev/stderr(FD #2). In practice, it could be a pipe, socket or whatever.

所以,从一开始。假设我们有终端连接到 /dev/stdout(FD #1) 和 /dev/stderr(FD #2)。实际上,它可以是管道、套接字或其他任何东西。

  • Create FDs #3 and #4 and point to the same "location" as #1 and #2 respectively. Changing FD #1 doesn't affect FD #3 from now on. Now, FDs #3 and #4 point to STDOUT and STDERR respectively. These will be used as realterminal STDOUT and STDERR.
  • 1> >(...) redirects STDOUT to command in parens
  • parens(sub-shell) executes 'tee' reading from exec's STDOUT(pipe) and redirects to 'logger' command via another pipe to sub-shell in parens. At the same time it copies the same input to FD #3(terminal)
  • the second part, very similar, is about doing the same trick for STDERR and FDs #2 and #4.
  • 创建 FD #3 和 #4 并分别指向与 #1 和 #2 相同的“位置”。从现在开始,更改 FD #1 不会影响 FD #3。现在,FD #3 和 #4 分别指向 STDOUT 和 STDERR。这些将用作真正的终端 STDOUT 和 STDERR。
  • 1> >(...) 将 STDOUT 重定向到括号中的命令
  • parens(sub-shell) 执行从 exec 的 STDOUT(pipe) 读取的“tee”,并通过另一个管道重定向到“logger”命令到 parens 中的 sub-shell。同时它将相同的输入复制到 FD #3(终端)
  • 第二部分非常相似,是关于对 STDERR 和 FD #2 和 #4 执行相同的技巧。

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

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

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

...is as follows:

...如下:

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

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

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

如果您想看到更清晰的图片,请将以下两行添加到脚本中:

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

回答by Guemundur H

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

1>file.loginstructs the shell to send STDOUT to the file file.log, and 2>&1tells it to redirect STDERR (file descriptor 2) to STDOUT (file descriptor 1).

1>file.log指示外壳将 STDOUT 发送到文件file.log,并2>&1告诉它重定向 STDERR(文件描述符 2)到 STDOUT(文件描述符 1)。

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

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

回答by Guemundur H

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

回答by Evan Rosica

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

简答:Command >filename 2>&1Command &>filename



Explanation:

解释:

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

考虑以下代码,它将单词“stdout”打印到 stdout,将单词“stderror”打印到 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 stdoutto stdout, and create a file named "2" and write stderrorthere.

请注意,'&' 运算符告诉 bash 2 是文件描述符(指向 stderr)而不是文件名。如果我们省略 '&',该命令将打印stdout到标准输出,并创建一个名为“2”的文件并stderror在那里写入。

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/nullthe following two lines of code delete everything from the stdout, and everything from stderror respectively (printing what remains).

通过试验上面的代码,您可以亲眼看到重定向操作符是如何工作的。例如,通过更改两个描述符中的哪个文件1,2,重定向到/dev/null以下两行代码分别删除 stdout 中的所有内容和 stderror 中的所有内容(打印剩余内容)。

$ (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/nullfirst (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 fileas file descriptor 1 (and not to file descriptor 1 itself!!!! (see this resource on pointersfor 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.

要真正理解这一点,我强烈建议您阅读有关文件描述符表的网页。假设您已完成阅读,我们可以继续。注意 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,这就是不呈现输出的原因。



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>&1does 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/nullwould give the pointer chain 2 -> 1 -> /dev/null, and thus the code would generate nothing, in contrast to what we saw above.

这里的推理是从左到右计算,Bash 看到 2>&1,因此将文件描述符 2 设置为与文件描述符 1 指向相同的位置,即 stdout。然后它将文件描述符 1(记住 >/dev/null = 1>/dev/null)设置为指向 >/dev/null,从而删除通常会发送到标准输出的所有内容。因此,我们剩下的就是没有发送到子shell中的标准输出(括号中的代码)-即“标准错误”。有趣的是,即使 1 只是一个指向标准输出的指针,将指针 2 重定向到 12>&1并不会形成一个指针链 2 -> 1 -> 标准输出。如果是这样,由于将 1 重定向到 /dev/null,代码2>&1 >/dev/null会给指针链 2 -> 1 -> /dev/null,因此代码不会产生任何东西,与我们上面看到的相反。



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/nullor in case of my example:

这里的第 3.6.4 节,我们看到我们可以使用运算符&>重定向 stdout 和 stderr。因此,要将任何命令的 stderr 和 stdout 输出重定向到\dev\null(删除输出),我们只需键入 $ command &> /dev/null或在我的示例中:

$ (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/nullis != >/dev/null 2>&1. One generates output and the other does not!
  • 文件描述符的行为类似于指针(尽管文件描述符与文件指针不同)
  • 将文件描述符“a”重定向到指向文件“f”的文件描述符“b”,会导致文件描述符“a”指向与文件描述符b - 文件“f”相同的位置。它不会形成一个指针链 a -> b -> f
  • 由于上述原因,顺序很重要,2>&1 >/dev/null是 != >/dev/null 2>&1。一个生成输出,另一个不生成!


Finally have a look at these great resources:

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

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

对重定向猛砸文档文件描述表的说明介绍指针

回答by Evan Rosica

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 stdOut & stderr to syslog.

它是相关的:将 stdOut 和 stderr 写入 syslog。

It almost work, but not from xinted ;(

它几乎可以工作,但不是来自 xinted ;(

回答by bebbo

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.

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

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
  • 首先交换 stderr 和 stdout
  • 然后将标准输出附加到日志文件
  • 管道 stderr 到 tee 并将其附加到日志文件中

回答by dmitry_podyachev

For 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

This bash-based solutions can pipe STDOUT and STDERR separately (from STDERR of "sort -c" or from STDERR to "sort -h").

这种基于 bash 的解决方案可以分别通过管道传输 STDOUT 和 STDERR(从“sort -c”的 STDERR 或从 STDERR 到“sort -h”)。