什么是数据流重定向
在 bash 中,执行命令的数据输入输出情况为:
通常情况下,输出结果都会输出到屏幕上,但是如果想要保存输出结果就需要使用数据流重定向。数据流重定向可以帮助对输出的结果进行重新定位,使之到达特定的位置。上图中箭头上方还有 standard input,standard output,standard error output 等标识,这三者分别代表不同的含义。
standard output,standard error output
因为都是输出,因此这两者可以放在一块做个对比。
standard output 表示命令正确执行所返回的正确信息,standard error output 表示命令执行失败后返回的错误信息。
上图中说到,数据流重定向能够将 standard output 和 standard error output 分别传送到 screen/file/device 中,其中所涉及到的特殊字符为:
- standard input:代码为 0,使用 0</< 或 0<</<<。
- standard output:代码为 1,使用 1>/> 或 1>>/>>。
- standard error output:代码为 2,使用 2> 或 2>>。
比如要将当前目录内的文件信息转存到文件 fileinfo 中:
wood@ubuntu:/tmp/test$ ll
total 8
drwxrwxr-x 2 wood wood 4096 Apr 4 22:49 ./
drwxrwxrwt 15 root root 4096 Apr 4 22:49 ../
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filab
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filac
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filad
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filea
wood@ubuntu:/tmp/test$ ll > ./fileinfo
wood@ubuntu:/tmp/test$ ll
total 12
drwxrwxr-x 2 wood wood 4096 Apr 4 22:50 ./
drwxrwxrwt 15 root root 4096 Apr 4 22:49 ../
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filab
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filac
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filad
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filea
-rw-rw-r-- 1 wood wood 342 Apr 4 22:50 fileinfo
wood@ubuntu:/tmp/test$ cat fileinfo
total 8
drwxrwxr-x 2 wood wood 4096 Apr 4 22:50 ./
drwxrwxrwt 15 root root 4096 Apr 4 22:49 ../
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filab
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filac
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filad
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filea
-rw-rw-r-- 1 wood wood 0 Apr 4 22:50 fileinfo
通过上边的结果,我们可以看出:
- 数据流重定向后不会输出结果到屏幕
- 定向文件不存在则自动建立
- 文件存在则会先将文件内容清空,再写入
如果需要将数据进行累加,则需要使用 >> 符号:
wood@ubuntu:/tmp/test$ ll >> ./fileinfo
wood@ubuntu:/tmp/test$ cat fileinfo
total 8
drwxrwxr-x 2 wood wood 4096 Apr 4 22:50 ./
drwxrwxrwt 15 root root 4096 Apr 4 22:49 ../
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filab
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filac
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filad
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filea
-rw-rw-r-- 1 wood wood 0 Apr 4 22:50 fileinfo
total 12
drwxrwxr-x 2 wood wood 4096 Apr 4 22:50 ./
drwxrwxrwt 15 root root 4096 Apr 4 22:54 ../
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filab
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filac
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filad
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filea
-rw-rw-r-- 1 wood wood 342 Apr 4 22:50 fileinfo
standard error output 的数据流重定向方法与 standard output 相似,只是对应的符号不同。如果需要同时重定向 standard output 和 standard error output 的话,就需要使用:
command > dir1 2> dir2
上边的命令是将正确信息和错误信息分别定向到不同位置,如果两者的位置一样,为了防止出现存储顺序错误,就需要使用特殊的写法:
command > dir 2>&1
command &> dir
上边的两种写法都是可行的。
/dev/null
从上图中我们知道,数据流可以定向到设备中,在 Linux 中存在一个很特殊的设备,该设备就是 /dev/null。该设备会吸收一切定向到该设备的信息:
wood@ubuntu:/tmp/test$ ll > /dev/null
wood@ubuntu:/tmp/test$ cat /dev/null
虽然直接使用 cat 命令查看一个设备这样的命令看起来很奇怪,但是结果却是和我们想象的完全一样。
standard input
standard input 可以对比 standard output 来看,是将输入定向到某一个位置,也就是数据的来源问题。
其实大多数命令除了命令名外,还需要我们手动地输入某些选项或者参数,对于这些选项和参数,也就可以从输入设备导入:
wood@ubuntu:/tmp/test$ cat > cmdarg
fileinfowood@ubuntu:/tmp/test$ cat >> fileinfo < ./cmdarg
wood@ubuntu:/tmp/test$ cat fileinfo
total 8
drwxrwxr-x 2 wood wood 4096 Apr 4 22:50 ./
drwxrwxrwt 15 root root 4096 Apr 4 22:49 ../
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filab
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filac
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filad
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filea
-rw-rw-r-- 1 wood wood 0 Apr 4 22:50 fileinfo
total 12
drwxrwxr-x 2 wood wood 4096 Apr 4 22:50 ./
drwxrwxrwt 15 root root 4096 Apr 4 22:54 ../
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filab
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filac
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filad
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filea
-rw-rw-r-- 1 wood wood 342 Apr 4 22:50 fileinfo
fileinfo
在上边的程序中,我们首先输入文件内容到 cmdarg,然后再将文件内容添加到 fileinfo 中,输出的结果和我们考虑的也是一致的。
由于没有输入换行,格式可能会有点不太整齐。
我们之前提到过 >> 和 2> 分别表示追加定向,而 << 的含义就不太一样了,<< 表示结束的输入字符,比如:
wood@ubuntu:/tmp/test$ cat > filee << "xxx"
> filea
> fileb
> filec
> filed
> filee
> xxx
wood@ubuntu:/tmp/test$ cat filee
filea
fileb
filec
filed
filee
上边的结果表示,<< 可以设置我们手动输入时的结束字符值,而不会把设置的字符写入到定向位置中。
几个特殊符号
;
在 bash 的操作环境中,我们提到过该符号可以用来分隔连续执行的命令:
wood@ubuntu:/tmp/test$ ls
filab filac filad filea filee
wood@ubuntu:/tmp/test$ ll
total 12
drwxrwxr-x 2 wood wood 4096 Apr 4 23:25 ./
drwxrwxrwt 15 root root 4096 Apr 4 23:25 ../
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filab
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filac
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filad
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filea
-rw-rw-r-- 1 wood wood 30 Apr 4 23:25 filee
wood@ubuntu:/tmp/test$ ls;ll
filab filac filad filea filee
total 12
drwxrwxr-x 2 wood wood 4096 Apr 4 23:25 ./
drwxrwxrwt 15 root root 4096 Apr 4 23:25 ../
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filab
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filac
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filad
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filea
-rw-rw-r-- 1 wood wood 30 Apr 4 23:25 filee
$?,||,&&
在 shell 中的变量我们提到过 ? 也是一个特殊的变量,该符号表示上一个命令的返回值,命令正常执行则为0,反之则为非0:
wood@ubuntu:/tmp/test$ ls
filab filac filad filea filee
wood@ubuntu:/tmp/test$ echo ${?}
0
因此可以借由这个返回值,实现一个条件式的命令执行:
wood@ubuntu:/tmp/test$ ls && ll
filab filac filad filea filee
total 12
drwxrwxr-x 2 wood wood 4096 Apr 4 23:25 ./
drwxrwxrwt 15 root root 4096 Apr 4 23:25 ../
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filab
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filac
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filad
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filea
-rw-rw-r-- 1 wood wood 30 Apr 4 23:25 filee
wood@ubuntu:/tmp/test$ abc && ll
No command 'abc' found, did you mean:
Command 'ab' from package 'apache2-utils' (main)
Command 'ajc' from package 'aspectj' (universe)
Command 'nbc' from package 'nbc' (universe)
Command 'ac' from package 'acct' (main)
Command 'alc' from package 'amule-utils-gui' (universe)
Command 'axc' from package 'afnix' (universe)
Command 'asc' from package 'asc' (universe)
Command 'abe' from package 'abe' (universe)
Command 'atc' from package 'bsdgames' (universe)
Command 'abx' from package 'abx' (universe)
Command 'aec' from package 'libaec-tools' (universe)
Command 'cbc' from package 'coinor-cbc' (universe)
Command 'bc' from package 'bc' (main)
Command 'arc' from package 'arcanist' (universe)
Command 'arc' from package 'arc' (universe)
abc: command not found
wood@ubuntu:/tmp/test$ ls || ll
filab filac filad filea filee
wood@ubuntu:/tmp/test$ abc || ll
No command 'abc' found, did you mean:
Command 'asc' from package 'asc' (universe)
Command 'atc' from package 'bsdgames' (universe)
Command 'ab' from package 'apache2-utils' (main)
Command 'ac' from package 'acct' (main)
Command 'ajc' from package 'aspectj' (universe)
Command 'bc' from package 'bc' (main)
Command 'cbc' from package 'coinor-cbc' (universe)
Command 'abe' from package 'abe' (universe)
Command 'axc' from package 'afnix' (universe)
Command 'arc' from package 'arcanist' (universe)
Command 'arc' from package 'arc' (universe)
Command 'aec' from package 'libaec-tools' (universe)
Command 'alc' from package 'amule-utils-gui' (universe)
Command 'nbc' from package 'nbc' (universe)
Command 'abx' from package 'abx' (universe)
abc: command not found
total 12
drwxrwxr-x 2 wood wood 4096 Apr 4 23:25 ./
drwxrwxrwt 15 root root 4096 Apr 4 23:25 ../
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filab
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filac
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filad
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filea
-rw-rw-r-- 1 wood wood 30 Apr 4 23:25 filee
通过上边的程序示例,基本能搞清除 || 和 && 的使用方法:
cmd1 && cmd2 | cmd1 执行正常,执行 cmd2 cmd1 执行异常,不执行 cmd2 |
cmd1 || cmd2 | cmd1 执行正常,不执行 cmd2 cmd1 执行异常,执行 cmd2 |
利用 || 和 && 能够实现:
- 如果 A 成功就执行 B
- 如果 A 不成功就执行 B
- 如果 A 成功就不执行 B
- 如果 A 不成功就不执行 B
下面实现:
- 如果 A 成功就执行 B,A 不成功就执行 C
结构为:
A && B || C
A || C && B
采用第一种写法验证一下:
wood@ubuntu:/tmp/test$ ls && ll || rm -rf *
filab filac filad filea filee
total 12
drwxrwxr-x 2 wood wood 4096 Apr 4 23:25 ./
drwxrwxrwt 15 root root 4096 Apr 4 23:25 ../
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filab
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filac
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filad
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filea
-rw-rw-r-- 1 wood wood 30 Apr 4 23:25 filee
wood@ubuntu:/tmp/test$ ls
filab filac filad filea filee
wood@ubuntu:/tmp/test$ abc && ls || ll
No command 'abc' found, did you mean:
Command 'aec' from package 'libaec-tools' (universe)
Command 'axc' from package 'afnix' (universe)
Command 'atc' from package 'bsdgames' (universe)
Command 'asc' from package 'asc' (universe)
Command 'alc' from package 'amule-utils-gui' (universe)
Command 'ac' from package 'acct' (main)
Command 'ajc' from package 'aspectj' (universe)
Command 'nbc' from package 'nbc' (universe)
Command 'arc' from package 'arc' (universe)
Command 'arc' from package 'arcanist' (universe)
Command 'bc' from package 'bc' (main)
Command 'cbc' from package 'coinor-cbc' (universe)
Command 'abe' from package 'abe' (universe)
Command 'abx' from package 'abx' (universe)
Command 'ab' from package 'apache2-utils' (main)
abc: command not found
total 12
drwxrwxr-x 2 wood wood 4096 Apr 4 23:25 ./
drwxrwxrwt 15 root root 4096 Apr 4 23:25 ../
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filab
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filac
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filad
-rw-rw-r-- 1 wood wood 0 Apr 4 22:49 filea
-rw-rw-r-- 1 wood wood 30 Apr 4 23:25 filee