BASH Shell将stderr重定向到stdout(将stderr重定向到File)

时间:2020-01-09 10:43:12  来源:igfitidea点击:

如何在bash下将stderr重定向到stdout?
使用bash时如何将stderr重定向到文件?
此页面说明了bash I/O重定向,可用于在shell提示符下或者shell脚本中将stderr和stdout重定向到文件。

  • stdin从键盘或者程序获取输入。换句话说,数据进入程序。
  • stdout在屏幕或者文件上写信息。
  • stderr在屏幕或者文件上显示错误消息

Bash和其他现代Linux/Unix Shell提供了I/O重定向功能。
每个进程打开三个默认的标准文件(标准流):

了解I/O流编号

带有数字的Unix/Linux标准I/O流:

句柄名称描述
0stdin标准输入
1stdout标准输出
2stderr标准错误

重定向输出

假设您要将date命令的输出保存到文件中。
尝试:

$ command > output.txt
$ date > today.txt

使用cat命令查看today.txt文件的包含,运行:

$ cat today.txt

在上面的示例中,将创建一个名为today.txt的文件,如果您执行命令date并执行它,它将包含日期(您在屏幕上看到的内容)。
请注意,以上命令还将创建文件Today.txty(如果不存在),否则将其覆盖。
如果您想添加/添加,请尝试以下语法:

$ command1 >> filename
$ command2 >> filename
$ ls -l >> output.txt
$ date >> output.txt

简而言之,我们可以简单地使用>或者>>符号将标准输出重定向到文件。

将标准错误流重定向到文件

以下将程序错误消息重定向到名为error.log的文件:

$ program-name 2> error.log
$ command1 2> error.log

例如,使用grep命令在$HOME目录中进行递归搜索,并将所有错误(stderr)重定向到文件名grep-errors.txt,如下所示:

$ grep -R 'MASTER' $HOME 2> /tmp/grep-errors.txt
$ cat /tmp/grep-errors.txt

输出示例:

grep: /home/Hyman/.config/google-chrome/SingletonSocket: No such device or address
grep: /home/Hyman/.config/google-chrome/SingletonCookie: No such file or directory
grep: /home/Hyman/.config/google-chrome/SingletonLock: No such file or directory
grep: /home/Hyman/.byobu/.ssh-agent: No such device or address

将标准错误(stderr)和stdout重定向到文件

使用以下语法:

$ command-name &>file

我们也可以使用以下语法:

$ command > file-name 2>&1

我们也可以将stderr和stdout都写到两个不同的文件中。
让我们尝试之前的grep命令示例:

$ grep -R 'MASTER' $HOME 2> /tmp/grep-errors.txt 1> /tmp/grep-outputs.txt
$ cat /tmp/grep-outputs.txt

将stderr重定向到stdout到文件或者其他命令

这是另一个有用的示例,其中stderr和stdout都发送到more命令而不是文件:

# find /usr/home -name .profile 2>&1 | more

将stderr重定向到stdout

使用以下命令:

$ command-name 2>&1
$ command-name > file.txt 2>&1
## bash only ##
$ command2 &> filename
$ sudo find / -type f -iname ".env" &> /tmp/search.txt

重定向从左到右。
因此,顺序很重要。
例如:

command-name 2>&1 > file.txt ## wrong ##
command-name > file.txt 2>&1 ## correct ##

如何在Bash脚本中将stderr重定向到stdout

在AWS/Linode服务器中创建时,用于更新VM的示例Shell脚本:

#!/usr/bin/env bash
# Author - theitroad under GPL v2.x+
# Debian/Ubuntu Linux script for EC2 automation on first boot
# -----------------------------------------------------------
# My log file - Save stdout to $LOGFILE
LOGFILE="/root/logs.txt"
 
# My error file - Save stderr to $ERRFILE
ERRFILE="/root/errors.txt"
 
# Start it 
printf "Starting update process ... \n" 1>"${LOGFILE}"
 
# All errors should go to error file 
apt-get -y update 2>"${ERRFILE}"
apt-get -y upgrade 2>>"${ERRFILE}"
printf "Rebooting cloudserver ... \n" 1>>"${LOGFILE}"
shutdown -r now 2>>"${ERRFILE}"

我们的最后一个示例使用exec命令和FD以及trap和自定义bash函数:

#!/bin/bash
# Send both stdout/stderr to a /root/aws-ec2-debian.log file
# Works with Ubuntu Linux too.
# Use exec for FD and trap it using the trap
# See bash man page for more info
# Author:  theitroad under GPL v2.x+
# --------------------------------------------
exec 3>&1 4>&2
trap 'exec 2>&4 1>&3' 0 1 2 3
exec 1>/root/aws-ec2-debian.log 2>&1
 
# log message
log(){
	local m="$@"
	echo ""
	echo "*** ${m} ***"
	echo ""
}
 
log "$(date) @ $(hostname)"
## Install stuff ##
log "Updating up all packages"
export DEBIAN_FRONTEND=noninteractive
apt-get -y clean
apt-get -y update
apt-get -y upgrade
apt-get -y --purge autoremove
 
## Update sshd config ##
log "Configuring sshd_config"
sed -i'.BAK' -e 's/PermitRootLogin yes/PermitRootLogin no/g' -e 's/#PasswordAuthentication yes/PasswordAuthentication no/g'  /etc/ssh/sshd_config
 
## Hide process from other users ##
log "Update /proc/fstab to hide process from each other"
echo 'proc    /proc    proc    defaults,nosuid,nodev,noexec,relatime,hidepid=2     0     0' >> /etc/fstab
 
## Install LXD and stuff ##
log "Installing LXD/wireguard/vnstat and other packages on this box"
apt-get -y install lxd wireguard vnstat expect mariadb-server 
 
log "Configuring mysql with mysql_secure_installation"
SECURE_MYSQL_EXEC=$(expect -c "
set timeout 10
spawn mysql_secure_installation
expect \"Enter current password for root (enter for none):\"
send \"$MYSQL\r\"
expect \"Change the root password?\"
send \"n\r\"
expect \"Remove anonymous users?\"
send \"y\r\"
expect \"Disallow root login remotely?\"
send \"y\r\"
expect \"Remove test database and access to it?\"
send \"y\r\"
expect \"Reload privilege tables now?\"
send \"y\r\"
expect eof
")
 
# log to file #
echo "   $SECURE_MYSQL_EXEC   "
# We no longer need expect 
apt-get -y remove expect
 
# Reboot the EC2 VM
log "END: Rebooting requested @ $(date) by $(hostname)"
reboot

还要将stderr和stdout都发送到终端和日志文件吗?

尝试按如下所示使用tee命令:

command1 2>&1 | tee filename

这也是内部shell脚本的使用方法:

#!/usr/bin/env bash
{
   command1
   command2 | do_something
} 2>&1 | tee /tmp/outputs.log

总结

在本快速教程中,您了解了三个文件描述符stdin,stdout和stderr。
我们可以使用这些Bash描述符将stdout/stderr重定向到文件,反之亦然。

运算符描述示例
command> filename将标准输出重定向到文件filename。date> output.txt
command >> filename重定向并将stdout追加到文件文件名。ls -l >> dirs.txt
命令2>文件名将stderr重定向到文件文件名。du -ch/snaps/2> space.txt
命令2 >>文件名重定向stderr并将其追加到文件文件名。awk'{print $4}'input.txt 2 >> data.txt
command&> filenamecommand> filename 2>&1将stdout和stderr都重定向到文件名。grep -R foo/etc /&> out.txt
命令&>>文件名命令>>文件名2>&1将stdout和stderr都追加到文件名中。whois domain >> log.txt