将 bash 的输出重定向到日志文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20915326/
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
Redirecting the output of my bash to a log file
提问by Andrea Moro
I'm now executing a series of bash scripts and I'd like to capture the value of the command I run in an output file.
我现在正在执行一系列 bash 脚本,我想捕获我在输出文件中运行的命令的值。
I thought this was possible by combining my command with | tee -a outputfile.txt
but this is captured only for script 1.
我认为通过将我的命令与我的命令相结合是可能的,| tee -a outputfile.txt
但这仅针对脚本 1 捕获。
Script 2..4 etc are not appending the output to the file.
脚本 2..4 等不会将输出附加到文件中。
Looking around, output can be also redirected in this way - 1>&1
- but I am a bit confused as sometimes I see number 1 replaced by 2. I guess this has to do with the type of messaging. However, as I don't know how this output redirection is call I'm stuck in finding info.
环顾四周,输出也可以通过这种方式重定向 - 1>&1
- 但我有点困惑,因为有时我看到数字 1 被 2 替换。我想这与消息传递的类型有关。但是,由于我不知道这个输出重定向是如何调用的,所以我一直在寻找信息。
Any help? Thanks Andrea
有什么帮助吗?谢谢安德里亚
回答by Jonathan Leffler
Basic Techniques
基本技巧
There are multiple ways to achieve it:
有多种方法可以实现:
exec >outputfile.txt
command1
command2
command3
command4
This changes the standard output of the entire script to the log file.
这会将整个脚本的标准输出更改为日志文件。
My generally preferred way to do it is:
我通常首选的方法是:
{
command1
command2
command3
command4
} > outputfile.txt
This does I/O redirection for all the commands within the scope of the braces. Be careful, you have to treat both {
and }
as if they were commands; they cannot appear just anywhere. This does not create a sub-shell — which is the main reason I favour it.
这会对大括号范围内的所有命令进行 I/O 重定向。小心,你必须同时治疗{
和}
,如果他们的命令; 它们不能随处出现。这不会创建子外壳——这是我喜欢它的主要原因。
You can replace the braces with parentheses:
您可以用括号替换大括号:
(
command1
command2
command3
command4
) > outputfile.txt
You can be more cavalier about the placement of the parentheses than the braces, so:
与大括号相比,您可以对括号的放置更加傲慢,因此:
(command1
command2
command3
command4) > outputfile.txt
would also work (but do that with the braces and the shell will fail to find a command {command1
(unless you happen to have a file around that's executable and ...). This creates a sub-shell. Any variable assignments done within the parentheses will not be seen/accessible outside the parentheses. This can be a show-stopper sometimes (but not always). The incremental cost of a sub-shell is pretty much negligible; it exists, but you're likely to be hard-pressed to measure it.
也可以工作(但是用大括号这样做,shell 将无法找到命令{command1
(除非你碰巧有一个可执行文件和......)。这会创建一个子 shell。在括号内完成的任何变量赋值不会在括号外被看到/访问。有时(但并非总是如此)这可能是一个阻碍。子外壳的增量成本几乎可以忽略不计;它存在,但你可能很难来测量它。
There's also the long-hand way:
还有长线方法:
command1 >>outputfile.txt
command2 >>outputfile.txt
command3 >>outputfile.txt
command4 >>outputfile.txt
If you wish to demonstrate that you're a neophyte shell programmer, by all means use this technique. If you wish to be considered as a more advanced shell programmer, do not.
如果你想证明你是一个新手 shell 程序员,一定要使用这个技巧。如果您希望被视为更高级的 shell 程序员,请不要这样做。
Note that all the commands above redirect just standard output to the named file, leaving standard error going to the original destination (usually the terminal). If you want to get standard error to go to the same file, simply add 2>&1
(meaning, send file descriptor 2, standard error, to the same place as file descriptor 1, standard output) after the redirection for standard output.
请注意,上面的所有命令仅将标准输出重定向到命名文件,而将标准错误转到原始目的地(通常是终端)。如果你想让标准错误转到同一个文件,只需在2>&1
标准输出重定向后添加(意思是,将文件描述符2,标准错误,发送到与文件描述符1,标准输出相同的地方)。
Applying the Techniques
应用技术
Addressing questions raised in the comments.
解决评论中提出的问题。
By using the
2>&1 >> $_logfile
(as per my answer below) I got what I need, but now I do have also my echo ... in the output file. Is there a way to print them on screen as well as the file at the same time?
通过使用
2>&1 >> $_logfile
(根据我下面的回答),我得到了我需要的东西,但现在我在输出文件中也有我的 echo ... 。有没有办法同时在屏幕和文件上打印它们?
Ah, so you don't want everything to go to the file...that complicates things a bit. There are ways, of course; not necessarily straight-forward. I'd probably use exec 3>&1;
to set file descriptor 3 going to the same place as 1 (standard output — or use 3>&2
if I wanted the echoes to standard error) before the main redirection. Then I'd create a function echoecho() { echo "$*"; echo "$*" >&3; }
and I'd use echoecho Whatever
to do the echoing. When you're done with file descriptor 3 (if you're not about to exit, when the system will close it) you can close it with exec 3>&-
.
啊,所以你不想把所有东西都放到文件里……这让事情有点复杂。当然有办法;不一定是直截了当的。在主重定向之前,我可能会使用exec 3>&1;
将文件描述符 3 设置为与 1 相同的位置(标准输出 - 或者3>&2
如果我想要标准错误的回显)。然后我会创建一个函数echoecho() { echo "$*"; echo "$*" >&3; }
,我会echoecho Whatever
用来做回声。完成文件描述符 3 后(如果您不打算退出,则系统将关闭它时)您可以使用exec 3>&-
.
When you refer to
exec
that's supposed to be the command I'm executing in the individual script file I created and that I will execute in between the cycle right? (just have a look at my answer below to see how I have evolved the script). For the rest of the suggestion I completely lost you.
当您提到
exec
这应该是我在我创建的单个脚本文件中执行的命令时,我将在循环之间执行,对吗?(看看下面我的回答,看看我是如何改进脚本的)。对于剩下的建议,我完全失去了你。
No; I'm referring the the Bash (shell) built-in command exec
. It can be used to do I/O redirection permanently (for the rest of the script), or to replace the current script with a new program, as in exec ls -l
— which is probably a bad example.
不; 我指的是 Bash (shell) 内置命令exec
。它可用于永久执行 I/O 重定向(对于脚本的其余部分),或用新程序替换当前脚本,如exec ls -l
- 这可能是一个不好的例子。
I guess now I even more confused than when I started :) Would that be possible create a small example … so I can understand it better?
我想现在我比刚开始时更困惑:) 是否可以创建一个小例子...这样我可以更好地理解它?
The disadvantage of comments is that they're hard to format and limited in size. Those limitations are also benefits, but there comes a time when the answer has to be extended. Said time has arrived.
注释的缺点是它们难以格式化且大小有限。这些限制也是好处,但有时必须扩展答案。说的时候到了。
For the sake of discussion, I'm going to restrict myself to 2 commands instead of 4 as in the question (but this doesn't lose any generality). Those commands will be cmd1
and cmd2
, and in fact those are two different names for the same script:
为了讨论起见,我将把自己限制在 2 个命令而不是问题中的 4 个命令(但这不会失去任何一般性)。这些命令将是cmd1
and cmd2
,实际上它们是同一个脚本的两个不同名称:
#!/bin/bash
for i in {01..10}
do
echo "$ ./cmd1 trying to work
./cmd1: stdout 1 - trying to work
./cmd1: stderr 1 - error message
./cmd1: stdout 2 - trying to work
./cmd1: stderr 2 - error message
…
./cmd1: stdout 9 - trying to work
./cmd1: stderr 9 - error message
./cmd1: stdout 10 - trying to work
./cmd1: stderr 10 - error message
$
: stdout $i - $*"
echo "#!/bin/bash
_logfile="output.txt"
# Delete output file if exist
if [ -f $_logfile ];
then
rm $_logfile
fi
for file in ./shell/*
do
$file 2>&1 >> $_logfile
done
: stderr $i - error message" >&2
done
As you can see, this script writes messages to both standard output and standard error. For example:
如您所见,此脚本将消息写入标准输出和标准错误。例如:
$ bash ex1.sh
./shell/cmd1: stderr 1 - error message
./shell/cmd1: stderr 2 - error message
…
./shell/cmd1: stderr 9 - error message
./shell/cmd1: stderr 10 - error message
./shell/cmd2: stderr 1 - error message
./shell/cmd2: stderr 2 - error message
…
./shell/cmd2: stderr 9 - error message
./shell/cmd2: stderr 10 - error message
$
Now, from the answerposted by Andrea Morowe find:
现在,从Andrea Moro发布的答案中我们发现:
2>>$_logfile >>$_logfile
>>$_logfile 2>&1
I don't like the variable name starting _
; there's no need for it that I can see. This redirects errors to where standard output is (currently) going, and then redirects standard output to the log file. So, if the sub-directory shell
contains cmd1
and cmd2
, the output is:
我不喜欢变量名开头_
;没有必要,我可以看到。这会将错误重定向到标准输出(当前)所在的位置,然后将标准输出重定向到日志文件。因此,如果子目录shell
包含cmd1
and cmd2
,则输出为:
logfile="output.txt"
rm -f $logfile
for file in ./cmd1 ./cmd2
do
$file trying to work >> $logfile 2>&1
done
To get both standard output and standard error to the file, you have use one of:
要同时获取文件的标准输出和标准错误,您可以使用以下方法之一:
logfile="output.txt"
{
for file in ./cmd1 ./cmd2
do
$file trying to work
done
} >$logfile 2>&1
I/O redirections are generally processed from left to right, except that piping controls where standard output (and standard error if you use |&
) goes to before the I/O redirections are handled.
I/O 重定向通常从左到右进行处理,除了管道控制在|&
处理 I/O 重定向之前标准输出(以及标准错误,如果您使用)的去向。
Adapting this script to generate information to standard output as well as logging to the log file, there are a variety of ways of working. I'm assuming the shebang line is #!/bin/bash
from here on.
修改此脚本以生成标准输出的信息以及记录到日志文件,有多种工作方式。我假设shebang线是#!/bin/bash
从这里开始的。
logfile="output.txt"
for file in ./cmd1 ./cmd2
do
$file trying to work
done >$logfile 2>&1
This removes the log file if it exists (but less verbosely than before). Everything on standard output and standard error goes to the log file. We could also write:
这将删除存在的日志文件(但比以前更详细)。标准输出和标准错误上的所有内容都进入日志文件。我们也可以这样写:
logfile="output.txt"
rm -f $logfile
for file in ./cmd1 ./cmd2
do
echo $file $(date +'%Y-%m-%d %H:%M:%S')
$file trying to work >> $logfile 2>&1
done
Or the code could use parentheses in place of the braces with only minor differences in functionality that wouldn't affect this script materially. Or, indeed, in this case, we could use:
或者代码可以使用括号代替大括号,只有在功能上的细微差别不会对这个脚本产生实质性影响。或者,实际上,在这种情况下,我们可以使用:
exec 3>&1
logfile="output.txt"
for file in ./cmd1 ./cmd2
do
echo $file $(date +'%Y-%m-%d %H:%M:%S') >&3
$file trying to work
done >$logfile 2>&1
exec 3>&-
And it is not clear that the variable is necessary, but we'll leave it in place. Note that both these use 'clobbering' I/O redirection because they create the log file just once, which in turn means there was no need to remove it (though there might be reasons to do so — related to other users running the command beforehand and leaving a non-writable file behind, but then you should probably have a date-stamped log file anyway so that isn't a problem after all).
并不清楚该变量是否必要,但我们将保留它。请注意,这两个都使用“clobbering”I/O 重定向,因为它们只创建了一次日志文件,这反过来意味着不需要删除它(尽管可能有这样做的原因——与其他用户事先运行命令有关并留下一个不可写的文件,但是无论如何你应该有一个带日期戳的日志文件,所以这毕竟不是问题)。
Clearly, if we want to echo something to the original standard output as well as to the log file, we have to do something different as both standard error and standard output are going to the log file.
显然,如果我们想将某些内容回显到原始标准输出以及日志文件中,我们必须做一些不同的事情,因为标准错误和标准输出都将发送到日志文件中。
One option is:
一种选择是:
exec 3>&1
echoecho()
{
echo "$*"
echo "$*" >&3
}
logfile="output.txt"
for file in ./cmd1 ./cmd2
do
echoecho $file $(date +'%Y-%m-%d %H:%M:%S')
$file trying to work
done >$logfile 2>&1
exec 3>&-
Another option is:
另一种选择是:
$ bash ex3.sh
./cmd1 2014-01-07 14:57:13
./cmd2 2014-01-07 14:57:13
$ cat output.txt
./cmd1 2014-01-07 14:57:13
./cmd1: stdout 1 - trying to work
./cmd1: stderr 1 - error message
./cmd1: stdout 2 - trying to work
./cmd1: stderr 2 - error message
…
./cmd1: stdout 9 - trying to work
./cmd1: stderr 9 - error message
./cmd1: stdout 10 - trying to work
./cmd1: stderr 10 - error message
./cmd2 2014-01-07 14:57:13
./cmd2: stdout 1 - trying to work
./cmd2: stderr 1 - error message
./cmd2: stdout 2 - trying to work
./cmd2: stderr 2 - error message
…
./cmd2: stdout 9 - trying to work
./cmd2: stderr 9 - error message
./cmd2: stdout 10 - trying to work
./cmd2: stderr 10 - error message
$
Now file descriptor 3 goes to the same place as the original standard output. Inside the loop, both standard output and standard error go to the log file, but the echo … >&3
sends the standard output of echo
to file descriptor 3.
现在文件描述符 3 与原始标准输出位于同一位置。在循环内部,标准输出和标准错误都转到日志文件,但echo … >&3
将标准输出发送echo
到文件描述符 3。
If you want the same echoed output to go to both the redirected standard output and the original standard output, then you can use:
如果您希望将相同的回显输出同时发送到重定向的标准输出和原始标准输出,则可以使用:
./sc1.sh >> log.txt
./sc2.sh >> log.txt
./sc3.sh >> log.txt
The output from this was:
输出结果是:
#!/bin/bash
n=1
num_scripts=3
while test $n -le $num_scripts
do
script$n.sh >> log.txt
n=$[n+1]
done
This is roughly what I was saying in my comments, written out in full.
这大致就是我在评论中所说的,完整地写出来。
回答by Raiyan
Let's say you have scripts in files sc1.sh, sc2.sh, and sc3.sh and you want to write the outputs to a file named log.txt, then you can do the following :
假设您在文件 sc1.sh、sc2.sh 和 sc3.sh 中有脚本,并且您想将输出写入名为 log.txt 的文件,那么您可以执行以下操作:
command >> file
or, if you prefer to automate the process by writing another bash script to loop through the other bash scripts :
或者,如果您更喜欢通过编写另一个 bash 脚本来循环其他 bash 脚本来自动化该过程:
command > file
回答by Emerick Rogul
回答by rjv
Try
尝试
#!/bin/bash
_logfile="output.txt"
# Delete output file if exist
if [ -f $_logfile ];
then
rm $_logfile
fi
for file in ./shell/*
do
$file 2>&1 >> $_logfile
done
to append to the file.
附加到文件中。
##代码##clears the file every time,ie,writes from position 1.
每次都清除文件,即从位置 1 写入。
回答by Andrea Moro
Ok, thanks for all your contribute and inputs. Assembling several bits I got the script working pretty much as I need generating the output on a file.
好的,感谢您的所有贡献和投入。组装几个位,我让脚本工作得非常好,因为我需要在文件上生成输出。
In a nutshell the problem was the way how these script were called; an external ruby script was launching the shell scripts, probably creating a different shell environment all the time. As a result of this, the tee command was probably not appending the output to my file (explicitly declared at the end of every line) because it was existing. I'm not sure whether this is true.
简而言之,问题在于如何调用这些脚本;一个外部 ruby 脚本正在启动 shell 脚本,可能一直在创建不同的 shell 环境。因此,tee 命令可能没有将输出附加到我的文件(在每一行的末尾显式声明),因为它已经存在。我不确定这是否属实。
So my working solution is
所以我的工作解决方案是
##代码##However, as I said I've one more challenge ... I can't now see any output at all on screen, whereas I am keen to read at least my "echo" command just to give me an idea where I am in the script execution.
然而,正如我所说,我还有一个挑战......我现在在屏幕上根本看不到任何输出,而我至少热衷于阅读我的“echo”命令只是为了让我知道我在哪里脚本执行。
Thanks for your further suggestions on this.
感谢您就此提出进一步建议。