当使用nohup把一个命令放在后台运行时,一些内容出现在终端。
cp: error reading ‘/mnt/tt/file.txt’: Input/output error
cp: failed to extend ‘/mnt/tt/file.txt’: Input/output error
我想把这些内容保存到一个文件中。
在Linux(和其他操作系统)中有两个主要的输出流,标准输出(stdout)和标准错误(stderr)。错误信息,比如你显示的那些,会被打印到标准错误中。经典的重定向操作(command > file
)只重定向标准输出,所以标准错误仍然显示在终端上。要把stderr也重定向,你有几个选择。
1.将stdout重定向到一个文件,将stderr重定向到另一个文件。
command > out 2>error
2.将stdout重定向到一个文件(>out
),然后将stderr重定向到stdout(2>&1
)。
command >out 2>&1
3.将两者都重定向到一个文件(这并不是所有的shell都支持,例如bash
和zsh
支持,但sh
和ksh
不支持)。
command &> out
关于各种控制和重定向操作符的更多信息,见这里。
首先要注意的是,有几种方法取决于你的目的和shell,因此这需要对多个方面略有了解。此外,某些命令如time
和strace
默认将输出写入stderr,并且可能提供或不提供针对该命令的重定向方法。
重定向背后的基本理论是,由shell产生的进程(假设它是一个外部命令而不是shell内置的)通过fork()
和execve()
系统调用创建,在这之前,另一个系统调用dup2()
在execve()
发生之前执行必要的重定向。从这个意义上讲,重定向是继承自父级shell的。 m&>n
和m>n.txt
通知shell如何执行open()
和dup2()
系统调用 (参见 输入重定向如何工作, 重定向和管道的区别是什么, 和 &在输出重定向中到底意味着什么 )
最典型的,是通过Bourne-like shells中的2>
,如dash
(与/bin/sh
有符号链接)和bash
;第一个是默认的、符合POSIX标准的shell,另一个是大多数用户用于互动会话的。它们在语法和功能上有所不同,但幸运的是,错误流重定向的效果是一样的(除了"&> "非标准的)。如果是csh及其衍生物,stderr重定向在那里并不完全有效 。
我们再来看看2>
部分。有两件关键的事情要注意:>
指的是重定向运算符,在这里我们打开一个文件,2
整数代表stderr文件描述符;事实上,这正是POSIX标准对shell语言的重定向的定义第2.7节。
[n]redir-op word
对于简单的>
重定向,stdout
隐含了1
的整数,即echo Hello World > /dev/null
与echo Hello World 1>/dev/null
相同。注意,整数或重定向运算符不能加引号,否则shell不会识别它们,而会将其视为字面文本字符串。至于间距,重要的是整数要紧挨着重定向操作符,但文件可以紧挨着重定向操作符,也可以不紧挨着,即command 2>/dev/null
和command 2> /dev/null
可以正常工作。
shell中典型命令的简化语法是
command [arg1] [arg2] 2> /dev/null
这里的诀窍是,重定向可以出现在任何地方。也就是说,2> command [arg1]
和command 2> [arg1]
都有效。请注意,对于bash
shell来说,存在&>
的方法来同时重定向stdout和stderr流,但这也是bash特有的,如果你努力追求脚本的可移植性,它可能不起作用。参见Ubuntu Wiki和&>和2>&1之间有什么区别。
注意: >
重定向操作符截断一个文件并覆盖它,如果该文件存在。2>>
可用于将stderr
追加到文件。
如果你注意到,>
是指一个单一的命令。对于脚本,我们可以从外部重定向整个脚本的stderr流,如myscript.sh 2> /dev/null
或者我们可以使用exec内置。exec内置有能力重新连接整个shell会话的数据流,可以说,无论是交互式的还是通过脚本。比如说
#!/bin/sh
exec 2> ./my_log_file.txt
stat /etc/non_existing_file
在这个例子中,日志文件应该显示 "stat: cannot stat '/etc/non_existing_file':没有这样的文件或目录`。 然而,另一种方法是通过函数。正如kopciuszek在他的回答中指出的,我们可以在写函数声明时已经附上重定向,即
some_function(){
command1
command2
} 2> my_log_file.txt
诸如 "time "和 "strace "之类的命令,默认情况下会将其输出写到stderr。如果是time
命令,唯一可行的办法是将整个命令的输出重定向,即
time echo foo 2>&1 > file.txt
另外,如果你想分离输出,也可以重定向同步列表或子壳(如相关帖子所示)。
{ time sleep 1 2> sleep.stderr ; } 2> time.txt
其他命令,如strace
或dialog
提供了重定向stderr的方法。strace
有-o <filename.txt>
选项,可以指定输出的文件名。还有一个选项是为strace
看到的每个子进程写一个文本文件。dialog
命令将文本用户界面写到stdout,但输出到stderr,所以为了将其输出保存到变量(因为var=$(...)
和管道只接收stderr)我们需要交换文件描述符
result=$(dialog --inputbox test 0 0 2>&1 1>/dev/tty);
但此外,还有--output-fd
标志,我们也可以利用它。还有一种命名管道的方法。我建议阅读关于dialog
命令的链接文章,以了解发生了什么。