JPL*_*mme 77 bash shell logging
我有一个脚本,将由非技术用户以交互方式运行.该脚本将状态更新写入STDOUT,以便用户可以确保脚本运行正常.
我希望STDOUT和STDERR都重定向到终端(这样用户就可以看到脚本工作正常,看看是否有问题).我还希望将两个流重定向到日志文件.
我在网上看到了很多解决方案.有些不起作用,有些则非常复杂.我已经开发了一个可行的解决方案(我将作为答案输入),但它很笨拙.
完美的解决方案是一行代码,可以合并到任何脚本的开头,该脚本将两个流发送到终端和日志文件.
编辑:将STDERR重定向到STDOUT并将结果传递给tee工作,但这取决于用户记住重定向和管道输出.我希望日志记录是万无一失的(这就是为什么我希望能够将解决方案嵌入脚本本身.)
Pau*_*lin 132
使用"tee"重定向到文件和屏幕.根据您使用的shell,首先必须使用stderr将stderr重定向到stdout
./a.out 2>&1 | tee output
Run Code Online (Sandbox Code Playgroud)
要么
./a.out |& tee output
Run Code Online (Sandbox Code Playgroud)
在csh中,有一个名为"script"的内置命令可以捕获进入文件的所有内容.您可以通过键入"script"来启动它,然后执行您要捕获的任何操作,然后按control-D关闭脚本文件.我不知道sh/bash/ksh的等价物.
此外,由于您现在已经指出这些是您自己可以修改的sh脚本,您可以通过用括号或括号括起整个脚本来内部进行重定向,例如
#!/bin/sh
{
... whatever you had in your script before
} 2>&1 | tee output.file
Run Code Online (Sandbox Code Playgroud)
Mat*_*ice 16
the_cmd 1> >(tee stdout.txt ) 2> >(tee stderr.txt >&2 )
Run Code Online (Sandbox Code Playgroud)
这会分别重定向 stdout 和 stderr,并将stdout 和 stderr 的单独副本发送给调用者(可能是您的终端)。
在 zsh 中,在tee
s 完成之前,它不会继续执行下一条语句。
在 bash 中,您可能会发现最后几行输出出现在接下来出现的任何语句之后。
在任何一种情况下,正确的位都到正确的地方。
这是一个脚本(存储在 ./example 中):
#! /usr/bin/env bash
the_cmd()
{
echo out;
1>&2 echo err;
}
the_cmd 1> >(tee stdout.txt ) 2> >(tee stderr.txt >&2 )
Run Code Online (Sandbox Code Playgroud)
这是一个会话:
$ foo=$(./example)
err
$ echo $foo
out
$ cat stdout.txt
out
$ cat stderr.txt
err
Run Code Online (Sandbox Code Playgroud)
这是它的工作原理:
tee
进程都启动了,它们的标准输入被分配给文件描述符。因为它们包含在进程替换中,这些文件描述符的路径在调用命令中被替换,所以现在看起来像这样:the_cmd 1> /proc/self/fd/13 2> /proc/self/fd/14
the_cmd
运行,将 stdout 写入第一个文件描述符,将 stderr 写入第二个。
在 bash 情况下,一旦the_cmd
完成,以下语句立即发生(如果您的终端是调用者,那么您将看到提示出现)。
在 zsh 的情况下,一旦the_cmd
完成,shell 会等待两个tee
进程都完成后再继续。更多关于这里。
第一个tee
进程从the_cmd
的标准输出中读取,将标准输出的副本写回给调用者,因为这就是tee
它的作用。它的输出不会被重定向,所以它们会原封不动地返回给调用者
第二个tee
进程将它stdout
重定向到调用者的stderr
(这很好,因为它的 stdin 正在从the_cmd
的 stderr读取)。因此,当它写入其标准输出时,这些位将进入调用者的标准错误。
这使文件和命令输出中的 stderr 与 stdout 分开。
如果第一个 tee 写入任何错误,它们将同时显示在 stderr 文件和命令的 stderr 中,如果第二个 tee 写入任何错误,它们将仅显示在终端的 stderr 中。
Jas*_*des 14
差不多五年后......
我相信这是OP寻求的"完美解决方案".
这是一个可以添加到Bash脚本顶部的单行程序:
exec > >(tee -a $HOME/logfile) 2>&1
Run Code Online (Sandbox Code Playgroud)
这是一个演示其用途的小脚本:
#!/usr/bin/env bash
exec > >(tee -a $HOME/logfile) 2>&1
# Test redirection of STDOUT
echo test_stdout
# Test redirection of STDERR
ls test_stderr___this_file_does_not_exist
Run Code Online (Sandbox Code Playgroud)
(注意:这仅适用于Bash.它不适用于/ bin/sh.)
改编自这里 ; 根据我的判断,原始文件没有在日志文件中捕获STDERR.修复了这里的注释.
编辑:我发现我出轨了,最终回答了与提出的问题不同的问题。真正问题的答案位于保罗·汤布林答案的底部。(如果您想增强该解决方案以出于某种原因分别重定向 stdout 和 stderr,您可以使用我在此处描述的技术。)
我一直想要一个保留 stdout 和 stderr 之间区别的答案。不幸的是,迄今为止给出的所有保留这种区别的答案都容易出现种族问题:正如我在评论中指出的那样,它们冒着程序看到不完整输入的风险。
我想我终于找到了一个保留区别的答案,不存在种族倾向,而且也不是非常繁琐。
第一个构建块:交换 stdout 和 stderr:
my_command 3>&1 1>&2 2>&3-
Run Code Online (Sandbox Code Playgroud)
第二个构建块:如果我们只想过滤(例如 tee)stderr,我们可以通过交换 stdout 和 stderr,过滤,然后交换回来来实现:
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
Run Code Online (Sandbox Code Playgroud)
现在剩下的很简单:我们可以在开头添加一个标准输出过滤器:
{ { my_command | stdout_filter;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
Run Code Online (Sandbox Code Playgroud)
或者在最后:
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
Run Code Online (Sandbox Code Playgroud)
为了让自己相信上述两个命令都有效,我使用了以下命令:
alias my_command='{ echo "to stdout"; echo "to stderr" >&2;}'
alias stdout_filter='{ sleep 1; sed -u "s/^/teed stdout: /" | tee stdout.txt;}'
alias stderr_filter='{ sleep 2; sed -u "s/^/teed stderr: /" | tee stderr.txt;}'
Run Code Online (Sandbox Code Playgroud)
输出是:
...(1 second pause)...
teed stdout: to stdout
...(another 1 second pause)...
teed stderr: to stderr
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,我的提示在“”之后立即返回teed stderr: to stderr
。
关于 zsh 的脚注:
上面的解决方案在 bash 中有效(也许还有其他一些 shell,我不确定),但在 zsh 中不起作用。zsh 失败的原因有两个:
2>&3-
zsh 无法理解该语法;必须重写为2>&3 3>&-
因此,例如,我的第二个解决方案必须为 zsh 重写为{my_command 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stderr_filter;} 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stdout_filter
(它也适用于 bash,但非常冗长)。
另一方面,您可以利用 zsh 神秘的内置隐式 tee 来获得更短的 zsh 解决方案,它根本不运行 tee:
my_command >&1 >stdout.txt 2>&2 2>stderr.txt
Run Code Online (Sandbox Code Playgroud)
(我不会从我发现的文档中猜到 和>&1
是2>&2
触发 zsh 隐式发球的东西;我通过反复试验发现了这一点。)
将 stderr 重定向到 stdout 在您的命令中附加此:2>&1
对于输出到终端并登录到文件,您应该使用tee
两者放在一起看起来像这样:
mycommand 2>&1 | tee mylogfile.log
Run Code Online (Sandbox Code Playgroud)
编辑:为了嵌入到你的脚本中,你会做同样的事情。所以你的脚本
#!/bin/sh
whatever1
whatever2
...
whatever3
Run Code Online (Sandbox Code Playgroud)
最终会变成
#!/bin/sh
( whatever1
whatever2
...
whatever3 ) 2>&1 | tee mylogfile.log
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
60896 次 |
最近记录: |