数据流重导向
一般来说,执行一个命令时的流程如下所示:它会先从文件中读入数据,经过处理之后再将数据输出到屏幕上。
其中,standard input 表示的是标准输入,默认时从键盘中输入;standard output(stdout) 表示的是标准输出,是指指令执行所回传的正确信息;standard error output (stderr)表示的是标准错误输出,是指指令执行失败后,所回传的错误信息。比如,当我们使用 cat 指令查询 /etc/crontab 这个文件时,假如这个文件存在的话,那么此时文件内容会显示到屏幕上,而这个就是标准输出;当这个文件不存在的话,那么屏幕上就是显示错误信息,而这个就是标准错误输出。
那么,数据流重导向就是指将命令在终端的标准输出、标准出错重定向到别的地方比如文件中,或者读取文件的内容来取代标准输入。改变数据流重导向所用的特殊符号如下所示:
- 标准输入 (stdin) :代码为 0 ,使用 < 或 <<(结束输入的意思)
- 标准输出 (stdout):代码为 1 ,使用 >(以覆盖的方式将数据输出到指定的文件或设备上) 或 >>(以累加的方式将数据输出到指定的文件或设备上)
- 标准错误输出(stderr):代码为 2 ,使用 2> (同上,2 和 > 之间没有空格)或 2>>(同上)
下面先看下 stdout 和 stderr 的重导向:
# 将 ls -lh 的标准输出存到 stdout 这个文件中。如果原本没有这个文件,则创建这个文件;如果原本有这个文件,则将这个文件的内容全部替换。
ls -lh > stdout
ls -lh 1> stdout # 此时需要注意 1 和 > 需要紧贴在一起
# 将 ls -lh 的标准输出追加到 stdout 这个文件中。如果原本没有这个文件,则创建这个文件;如果原本有这个文件,则将输出的内容追到这个文件原有内容的末尾。
ls -lh >> stdout
ls -lh 1>> stdout
# 将 ls -lh 的标准错误存到 stderr 这个文件中。
ls -lh 2> stderr
# 将 ls -lh 的标准输出存到 stdout 中,标准错误输出存到 stderr 中。
ls -lh > stdout 2> stderr # 如果仅存在 > 时,则代表默认的代码 1
ls -lh 1> stdout 2> stderr
# 将 ls -lh 的标准输出存到 stdout 中,标准错误存到 /dev/null。/dev/null 可以吃掉任何导向这个设备的信息。
ls -lh 1> stdout 2> /dev/null
# 将 ls -lh 的标准输出和错误都输入到 stdouterr 这个文件中。
ls -lh 1> stdouterr 2>&1 # 推荐使用这种
ls -lh &> stdouterr
# ls -lh 1> stdouterr 2> stdouterr,这种方式无法实现上述的效果。因为都是直接覆盖的,虽然可以使用追加的方式,但是这两种数据会交叉写入到该文件中。
再看下 stdin 的重导向,stdin 的重导向就是将原本从键盘输入的数据,改由文件内容来取代。
# 这里使用了两个重导向,一种是标准输出的重导向:cat > catfile 表示标准输出存到 catfile 这个文件中。之后加了一个 < .bashrc 表示将 .bashrc 这个文件的内容作为标准输入输进去。最终实现的是将 .bashrc 这个文件的内容传到 catfile 这个文件中。
cat > catfile < .bashrc
# << 代表的是“结束的输入字符”,比如下面这条命令则表示将键盘输入的信息输出到 catfile 文件中,且当键盘输入 eof 时结束该次输入。也就是说可以利用 << 来终止一次输入,而不必使用 ctrl+d 的方式来结束输入。
$ cat > catfile << "eof"
heredoc> demo
heredoc> demo
heredoc> demo
heredoc> eof # 输入 eof 关键字之后就会停止输入了
命令执行的判断依据
# 1. 用分号连接。命令之间没有关联,分号前的指令执行完后就会立即接着执行后面的指令。
cmd1; cmd2; cmd3
# 2. 用 && 连接。若 cmd1 执行完毕且正确执行($?=0),则开始执行 cmd2。若 cmd1 执行完毕且为错误 ($?≠0),则 cmd2 不执行。
cmd1 && cmd2
# 3. 用 || 连接。若 cmd1 执行完毕且正确执行($?=0),则 cmd2 不执行。若 cmd1 执行完毕且为错误 ($?≠0),则开始执行 cmd。
cmd1 || cmd2
# ls 查阅目录 /tmp/abc 是否存在,如果存在那么 $? 为 0,那么则使用 touch 创建 /tmp/abc/hehe
ls /tmp/abc && touch /tmp/abc/hehe
# 假如 /tmp/abc 不存在那么该指令执行回传 $?!=0,那么此时会执行 mkdir 这条指令。由于 mkdir 这条指令会执行成功,所以 touch 也会被执行。
# 假如 /tmp/abc 存在,那么该指令执行回传 $?=0,此时不会执行 mkdir 这条指令。此时 $?=0 这个结果会继续向后传,此时 touch 也会被执行。
ls /tmp/abc || mkdir /tmp/abc && touch /tmp/abc/hehe
# ls 测试 /tmp/abc 存不存在,如果存在的话那么则显示 exist,假如不存在的话,那么则显示 not exist。
# 一般来说,假如想要使用 && 和 || 实现判断,那么这两个的顺序是不能颠倒的。一般来说,顺序应该是:command1 && command2 || command3。
ls /tmp/abc && echo "exist" || echo "not exist"
# ls /tmp/abc || echo "not exist" && echo "exist" 这种顺序则是错误的,因为假设 /tmp/abc 不存在,那么会回传 $?!=0 那么第一个 echo 将会执行,并且肯定是执行成功的,此时 $?=0,因此第二个 echo 仍然会执行。这就违背了当初想要的效果了。