Bash的基本特性之输入输出重定向

输入输出重定向

输入重定向指的是将原来从标准输入读取数据的位置重新定向为从其他位置读取数据,输出重定向指的是把原来要输出到标准输出的内容,重新定向输出到指定的其他位置。

输入输出重定向通常与文件描述符 FD 有关,shell 的 FD 通常为10个,即 0~9,最常用的有 3 个,即 0(stdin)、1(stdout)、2(stderr)。

重定向操作符

命令从键盘的输入读入数据是很常见的的。

1
2
3
[user1@study ~]$ read -p 'Input something: ' line ; echo "Your input : ${line}"
Input something: Tom is 15 years old.
Your input : Tom is 15 years old.

< 来改变读进的数据位置(stdin),使之从指定的位置读进。0 是 < 的默认值,因此 <0< 是一样的。

1
2
3
4
[user1@study ~]$ read -p 'Input something: ' line < file
[user1@study ~]$ echo "Your input : ${line}"
Your input : Good good study, day day up!
[user1@study ~]$

> 来改变送出位置,使之输出到指定的位置。1 是 > 的默认值,因此 >1> 是一样的。如果指定的文件不存在则自动创建这个文件并以覆盖方式写入数据,如果文件存在将会把命令的输出覆盖掉文件内容。所以 > 也叫覆盖重定向。

1
2
3
4
5
6
7
8
9
10
11
 [user1@study ~]$ date +"Today: %F %T"
Today: 2015-07-13 21:44:25
[user1@study ~]$ ls today.txt
ls: cannot access today.txt: No such file or directory
[user1@study ~]$ date +"Today: %F %T" > today.txt
[user1@study ~]$ cat today.txt
Today: 2015-07-13 21:44:31
[user1@study ~]$ echo Good > today.txt
[user1@study ~]$ cat today.txt
Good
[user1@study ~]$

>> 来改变送出位置,使之输出到指定的位置。1 是 >> 的默认值,因此 >>1>> 是一样的。如果指定的文件不存在则自动创建这个文件并以追加的方式写入数据,如果文件存在将会把命令的输出追加到文件中。所以 >> 也叫追加重定向。

1
2
3
4
5
6
7
  [user1@study ~]$ cat file.txt 
Hello world !
[user1@study ~]$ echo $(date) >> file.txt
[user1@study ~]$ cat file.txt
Hello world !
Tue Jul 14 15:00:10 CST 2015
[user1@study ~]$

如果想要对标准错误输出进行重定向,需要使用其文件描述符 2 配合重定向操作符 >>> 来使用,即 2>2>> 。这在记录日志或者在调试的时候显得十分有用。

1
2
3
4
5
6
7
8
[user1@study ~]$ ls abc 2> log1.txt
[user1@study ~]$ cat log1.txt
ls: cannot access abc: No such file or directory
[user1@study ~]$ cat bcd 2>> log1.txt
[user1@study ~]$ cat log1.txt
ls: cannot access abc: No such file or directory
cat: bcd: No such file or directory
[user1@study ~]$

但是要注意文件描述符和重定向操作符必须紧挨着,不能有空格,否则将无法完成重定向。

1
2
3
4
[user1@study ~]$ cat bcd 2 >> log1.txt 
cat: bcd: No such file or directory
cat: 2: No such file or directory
[user1@study ~]$

2>&1 将会把标准错误输出重定向到标准输出。

1
2
3
4
5
[user1@study ~]$ head 123.txt >> log1.txt 2>&1 
[user1@study ~]$ cat log1.txt
ls: cannot access abc: No such file or directory
cat: bcd: No such file or directory
head: cannot open ‘123.txt’ for reading: No such file or directory

你可以理解为不管是正确输出还是错误输出,都并到一个数据流来处理。

1
2
3
4
5
6
7
8
[user1@study ~]$ echo '123' > 123.txt
[user1@study ~]$ head 123.txt >> log1.txt 2>&1
[user1@study ~]$ cat log1.txt
ls: cannot access abc: No such file or directory
cat: bcd: No such file or directory
head: cannot open ‘123.txt’ for reading: No such file or directory
123
[user1@study ~]$

&> 将会把标准错误输出已覆盖的方式重定向一个文件或设备,用起来基本上和 command > filename 2>&1 效果是一样的

1
2
3
4
5
6
7
8
9
[user1@study ~]$ cat log1.txt 
[user1@study ~]$
[user1@study ~]$ echo 'yes' &> log1.txt
[user1@study ~]$ cat log1.txt
yes
[user1@study ~]$ eco &> log1.txt
[user1@study ~]$ cat log1.txt
-bash: eco: command not found
[user1@study ~]$

重定向时还可以对标准输出和标准错误输出分别指定各自的输出位置。

1
2
3
4
5
6
7
 [user1@study ~]$ eco 'yes' > info.log 2> err.log  
[user1@study ~]$ cat err.log
-bash: eco: command not found
[user1@study ~]$ echo 'yes' > info.log 2> err.log
[user1@study ~]$ cat info.log
yes
[user1@study ~]$

在输入输出重定向中,stdout 与 stderr 的管道会先准备好,才会从 stdin 读进数据。因此不管命令输出是错误还是正确,在覆盖重定时文件时都会先被清空。

1
2
3
4
5
6
7
8
9
10
11
12
[user1@study ~]$ cat file
Good good study, day day up!
[user1@study ~]$ date +"%F %T"
2015-07-13 21:54:12
[user1@study ~]$ date +%F %T
date: extra operand ‘%T’
Try 'date --help' for more information.
[user1@study ~]$ date +%F %T > file
date: extra operand ‘%T’
Try 'date --help' for more information.
[user1@study ~]$ cat file
[user1@study ~]$

/dev/null

/dev/null 称为空设备,是一个特殊的设备文件,它会丢弃一切写入其中的数据。/dev/null 常被程序员称为位桶(bit bucket)或者黑洞(black hole)。空设备通常被用于丢弃不需要的输出流,或作为用于输入流的空文件。当你读它的时候,它会提供无限的空字符。

1
2
3
[user1@study ~]$ ls *.txt &> /dev/null
[user1@study ~]$ ls *.txt > /dev/null 2>&1
[user1@study ~]$

在很多时候我们并期望看到命令的输出,只关心执行正确与否。此时可以做输出重定向,并且重定向到 /dev/null 即可

1
2
3
4
[user1@study ~]$ echo $(date) | grep 2015 &>/dev/null
[user1@study ~]$ echo $?
0
[user1@study ~]$

写在后面

有时候需要清空文件的内容而不是删除文件本身。

1
2
3
4
5
6
7
[user1@study ~]$ cat test.log 
run-parts(/etc/cron.daily)[1404]: finished logrotate
run-parts(/etc/cron.daily)[1392]: starting man-db.cron
run-parts(/etc/cron.daily)[1415]: finished man-db.cron
[user1@study ~]$ > test.log
[user1@study ~]$ cat test.log
[user1@study ~]$

这个功能看似没什么复杂的,但在实际工作过程中经常会因为粗心在追加重定向时漏写了一个 >,直接将文件覆盖了, 结果酿成了悲剧。

如何才能禁止覆盖一个已经存在的文件呢?Linux 中有一个内置的 shell 命令 set,结合它的选项 -C 就能实现这个功能。

1
2
3
4
[user1@study ~]$ set -C
[user1@study ~]$ > 123.txt
-bash: 123.txt: cannot overwrite existing file
[user1@study ~]$

如果百分之百确定要覆盖,可以使用 set +C 关闭这个功能等到操作完成之后再开启。

1
2
3
 [user1@study ~]$ set +C
[user1@study ~]$ echo '456' > 123.txt
[user1@study ~]$ set -C

或者使用 >| 在重定向时强进行制覆盖

1
2
3
4
5
[user1@study ~]$ set -C 
[user1@study ~]$ echo '456' >| 123.txt
[user1@study ~]$ echo '456' > 123.txt
-bash: 123.txt: cannot overwrite existing file
[user1@study ~]$

实际上 set -o noclobber 等同于 set -Cset +o noclobber 等同于 set +C

1
2
3
4
5
6
[user1@study ~]$ set -o noclobber
[user1@study ~]$ echo '567' > 123.txt
-bash: 123.txt: cannot overwrite existing file
[user1@study ~]$ set +o noclobber
[user1@study ~]$ echo '567' > 123.txt
[user1@study ~]$
有钱任性,请我吃包辣条
0%