cmd >>file.txt 2>&1
Bash会按照从左到右的顺序执行重定向,具体如下:
>>file.txt
:以追加模式打开file.txt
并将stdout
重定向至该文件。2>&1
:将stderr
重定向至"当前stdout
所指向的位置"。在这个例子中,那就是以追加模式打开的文件。换句话说,&1
重用了当前stdout
所使用的文件描述符。请注意,“将 STDERR 重定向到 STDOUT”这种解释是错误的。 - TheBonsaifile:将 STDOUT 重定向到文件(追加模式)(缩写为 1>>file) 2>&1:将 STDERR 重定向到“STDOUT 所在位置”
cmd >>file1 2>>file2
,它应该能实现你想要的效果。 - Woodrow Douglass有两种方法可以实现这个,具体取决于你所用的Bash版本。
传统和可移植(Bash 4之前)的方法是:
cmd >> outfile 2>&1
非便携的方式,从Bash 4开始是:
cmd &>> outfile
(类似于&> outfile
)
为了良好的编码风格,您应该:
如果你的脚本已经以#!/bin/sh
开始(无论是否有意),则Bash 4解决方案以及任何特定于Bash的代码都不适用。
还要记住,Bash 4 &>>
只是更短的语法 - 它不会引入任何新的功能或类似的东西。
该语法(除其他重定向语法之外)在Bash hackers wiki中描述。
sh
。你可以通过在crontab -e
文件中添加SHELL=/bin/bash
来更改默认的shell。 - Ray Fosscmd >log.out 2>log_error.out
添加将是:
cmd >>log.out 2>>log_error.out
cmd >log.out 2>&1
。我正在编辑我的回答以删除第一个示例。 - Aaron R.cmd > my.log 2> my.log
这个命令不起作用的原因是重定向从左到右逐步执行,首先 > my.log
的作用是“创建一个新文件my.log
并将标准输出重定向到该文件”,如果已存在同名文件则替换之。在此完成后,2> my.log
开始执行,它的作用也是“创建一个新文件my.log
并将标准错误输出重定向到该文件”,这将覆盖掉之前创建的同名文件。由于UNIX允许删除打开的文件,stdout现在被记录到了以前叫做my.log
但现在已被删除的文件中。一旦指向该文件的最后一个filehandle关闭,该文件的内容将被删除。 - Mikko Rantalainencmd > my.log 2>&1
能够工作是因为 > my.log
表示“创建新文件 my.log
替换现有文件并将 stdout
重定向到该文件”,在这个操作完成后,2>&1
表示“将文件句柄 2 指向文件句柄 1”。根据 POSIX 规则,文件句柄 1 总是 stdout,文件句柄 2 总是 stderr,因此 stderr
然后指向第一个重定向中已经打开的文件 my.log
。请注意,语法 >&
不会创建或修改实际文件,因此不需要使用 >>&
。(如果 第一个 重定向是 >> my.log
,那么文件将简单地以追加模式打开。) - Mikko Rantalainen这应该可以正常工作:
your_command 2>&1 | tee -a file.txt
它将所有日志存储在file.txt中,并在终端中转储它们。
试试这个:
You_command 1> output.log 2>&1
在Bash 4中,你使用的 &> x.file
是不起作用的。对此很抱歉 :(
在Bash中,0、1、2、……、9都是文件描述符。
0代表标准输入,1代表标准输出,2代表标准错误输出。3~9是为任何其他临时用途保留的。
可以使用操作符>
或>>
(追加)将任何文件描述符重定向到其他文件描述符或文件。
用法: <文件描述符> > <文件名 | &文件描述符>
请参见第20章 I/O重定向的参考资料。
You_command
的错误输出重定向到标准输出,并将You_command
的标准输出重定向到文件output.log
中。此外,它不会追加到文件,而是覆盖它。 - pabouk - Ukraine stay strong1 > output.log 2>&1
- Quintus.Zhou另一种方法:
如果使用旧版本的Bash,没有可用的&>>
,您也可以这样做:
(cmd 2>&1) >> file.txt
这种方法会生成一个子shell,因此它比传统的cmd >> file.txt 2>&1
方法效率更低,并且无法用于需要修改当前shell的命令(例如cd
、pushd
),但这种方法对我来说更自然和易懂:
另外,使用圆括号可以消除任何顺序上的歧义,特别是如果您想将标准输出和标准错误输出管道传输到另一个命令:
为了避免启动子shell,您可以使用花括号而不是圆括号来创建一个组命令:
{ cmd 2>&1; } >> file.txt
(注意,分号(或换行符)是必需的,以终止组命令。)
cmd >> file 2>&1
在所有 shell 中都可以工作,而且不需要多余的进程来运行。 - Mikko Rantalainencmd >> file 2>&1
还是 cmd 2>&1 >> file
,我认为使用 cmd 2>&1 | cat >> file
比使用大括号或者圆括号更容易。对于我来说,一旦你理解了 cmd >> file 2>&1
的实现实际上是 "将 STDOUT 重定向到 file
",然后是 "将 STDERR 重定向到当前 STDOUT 相应的 file(在第一个重定向之后显然是 file
)",就立即可以明白你应该按何种顺序进行重定向了。UNIX 不支持重定向到流,只能重定向到被流指针指向的 file 描述符。 - Mikko Rantalainen从“2>&1”是什么意思?开始,我将在这个答案中使用以下命令:
ls -ld /tmp /tnt
STDIN
和STDERR
。 (希望您的根目录中没有名为tnt
的条目。)
您可以计划从脚本本身进行重定向:
#!/bin/bash
exec 1>>logfile.txt
exec 2>&1
/bin/ls -ld /tmp /tnt
logfile.txt
文件,其中包含:/bin/ls: cannot access '/tnt': No such file or directory
drwxrwxrwt 2 root root 4096 Apr 5 11:20 /tmp
或者
#!/bin/bash
exec 1>>logfile.txt
exec 2>>errfile.txt
/bin/ls -ld /tmp /tnt
在创建或追加标准输出到logfile.txt
并创建或追加错误输出到errfile.txt
。
您可以创建两个不同的日志文件,追加到一个总体日志并重新创建另一个最后日志:
#!/bin/bash
if [ -e lastlog.txt ] ;then
mv -f lastlog.txt lastlog.old
fi
exec 1> >(tee -a overall.log /dev/tty >lastlog.txt)
exec 2>&1
ls -ld /tnt /tmp
#!/bin/bash
[ -e lastlog.txt ] && mv -f lastlog.txt lastlog.old
[ -e lasterr.txt ] && mv -f lasterr.txt lasterr.old
exec 1> >(tee -a overall.log combined.log /dev/tty >lastlog.txt)
exec 2> >(tee -a overall.err combined.log /dev/tty >lasterr.txt)
ls -ld /tnt /tmp
所以你有
lastlog.txt
上次运行的日志文件lasterr.txt
上次运行的错误文件lastlog.old
上一次运行的日志文件lasterr.old
上一次运行的错误文件overall.log
追加的总体日志文件overall.err
追加的总体错误文件combined.log
追加的总体错误和日志合并文件#!/bin/bash
sTime=${EPOCHSECONDS}
for pre in log err; do
printf -v ${pre}file '%s-%(%Y%m%d%H%M%S)T-%08x.txt' "$pre" "$sTime" $$
done
exec 1> >(tee -a overall.log combined.log /dev/tty >"$logfile")
exec 2> >(tee -a overall.err combined.log /dev/tty >"$errfile")
ls -ld /t{nt,mp}
$sTime
来确保两个文件的时间戳完全相同。(如果 $EPOCHSECONDS
被调用两次,它们之间可能会有差异!)$$
,以确保唯一性。第三次运行后,您必须找到 9 个文件:
ls -ltr
-rw-r--r-- 1 user user 49 19 nov 10:36 log-20231119103611-00120649.txt
-rw-r--r-- 1 user user 73 19 nov 10:36 err-20231119103611-00120649.txt
-rw-r--r-- 1 user user 73 19 nov 10:36 err-20231119103634-001207b8.txt
-rw-r--r-- 1 user user 49 19 nov 10:36 log-20231119103634-001207b8.txt
-rw-r--r-- 1 user user 147 19 nov 10:40 overall.log
-rw-r--r-- 1 user user 219 19 nov 10:40 overall.err
-rw-r--r-- 1 user user 49 19 nov 10:40 log-20231119104000-001216f0.txt
-rw-r--r-- 1 user user 73 19 nov 10:40 err-20231119104000-001216f0.txt
-rw-r--r-- 1 user user 366 19 nov 10:40 combined.log
stdbuf
:关于Fonic的评论,经过一些测试,我不得不同意:使用tee
,stdbuf
是无用的。 但是...
# Source this to multi-log your session
[ -e lasterr.txt ] && mv -f lasterr.txt lasterr.old
[ -e lastlog.txt ] && mv -f lastlog.txt lastlog.old
exec 2> >(exec stdbuf -i0 -o0 tee -a overall.err combined.log /dev/tty >lasterr.txt)
exec 1> >(exec stdbuf -i0 -o0 tee -a overall.log combined.log /dev/tty >lastlog.txt)
Once sourced this, you could try:
ls -ld /tnt /tmp
我使用了更复杂的命令来实时解析和重组squid
的日志:由于每行以毫秒为单位的UNIX EPOCH开头,我在第一个点上分割行,然后在EPOCH SECONDS之前添加@
符号,将它们传递给date -f - +%F\ %T
,然后使用paste -d .
将date
的输出和剩余的行重新组合。
exec {datesfd}<> <(:)
tail -f /var/log/squid/access.log |
tee >(
exec sed -u 's/^\([0-9]\+\)\..*/@\1/'|
stdbuf -o0 date -f - +%F\ %T >&$datesfd
) |
sed -u 's/^[0-9]\+\.//' |
paste -d . /dev/fd/$datesfd -
date
命令时,需要使用stdbuf
命令...exec
和stdbuf
命令的一些解释:
使用$(...)
或<(...)
运行forks
是通过运行子shell来执行另一个子shell(子子shell)来完成的。 exec
命令告诉shell脚本中没有其他命令需要运行,因此二进制文件(stdbuf ... tee
)将作为替代进程在相同级别执行(无需为运行另一个子进程保留更多内存)。
来自bash
的man页面(man -P'less +/^\ *exec\ ' bash
):
exec [-cl] [-a name] [command [arguments]]
如果指定了命令,则替换shell。不会创建新进程....
这并不是真正必要的,但可以减少系统的占用。
来自stdbuf
的man页面:
NAME stdbuf - 运行COMMAND,对其标准流进行修改缓冲操作。
这将告诉系统对tee
命令使用无缓冲I/O。因此,当有输入到来时,所有输出将立即更新。
exec stdbuf
如何在这个场景中有所帮助?stdbuf
的 man 手册指出它对 tee
没有影响,能否解释一下? - Fonicstdbuf
的 man 页面指出 tee
不会受到其影响,那么这有什么意义呢?引用内容如下:“注意:如果 COMMAND 调整其标准流的缓冲区(例如 'tee'),那么它将覆盖 'stdbuf' 所做出的相应更改。” - Fonic这太好了!
将输出重定向到当前脚本的日志文件和标准输出。
参考https://dev59.com/eXRC5IYBdhLWcg3wYP-h#314678,非常简单和干净,它将脚本的所有输出(包括在脚本中调用的脚本)都重定向到日志文件和标准输出:
通常我们会将其中一个放在脚本的顶部或附近。解析命令行的脚本在解析后进行重定向。exec > >(tee -a "logs/logdata.log") 2>&1将日志打印到屏幕并写入文件中 - shriyog于2017年2月2日9:20