每个进程都至少有3个信道:“标准输入”(stdin)、“标准输出”(stdout)和“标准出错”(stderr)。内核给每个进程都设置了这3个信道,所以进程本身不必知道这三个信道通到哪里。举例来说,它们可能连接到一个终端窗口、一条网络连接,或者属于另一个进程的信道。
UNIX有一个统一的I/O模型,在这个模型中,每个信道都以一个整数来命名,它叫做文件描述符。文件描述符0、1以及2是系统预留的。
0 —— stdin(标准输入)
1 —— stdout(标准输出)
2 —— stderr(标准错误)
大多数命令都接受从stdin来的输入,并且把自己的输出写到stdout,而把出错消息写到stderr。有了这样的约定,用户就能把命令像积木一样串起来,创建出混合管道。
shell将<、>和>>解释成指令,用来把一条命令的输入或者输出重定向到一个文件。<符号把这条命令的stdin和已有的某个文件的内容联系起来;>符号重定向stdout,会替换文件的现有内容;>>符号重定向stdout,会给文件追加内容。
例1:重定向或保存到一个文件中
echo "This is a test message1." > temp.txt
例2:将文本追加到目标文件中
echo "This is a test message2." >> temp.txt
为了把stdout和stderr都重定向到同一个地方,可以用>&这个符号。仅仅重定向stderr的话,则用2>。
命令find演示了想要分开处理stdout和stderr的原因,因为它会在两个信道提供输出,特别是以非特权用户身份运行的时候。例如,像下面这条命令:
find / -name core
有时候在输出中可能包含一些不必要的信息(比如调试消息),从而把真正的结果给淹没在混乱的输出里。要消除所有出错消息,可以用这条命令:
find / -name core 2> /dev/null
要把匹配路径的清单保存在一个文件里,可以使用如下命令:
find / -name core > temp.txt 2> /dev/null
这一行命令把匹配的路径发到temp.txt这个文件,丢弃出错消息,向终端窗口什么都不发。
要把一条命令的stdout连接到另一条命令的stdin上,可以用|这个符号,它常叫做管道。
例子:
ps -ef | grep httpd
这条命令运行ps产生一份进程清单,由管道送给grep命令选出包含httpd这个词的若干行。grep命令的输出没有重定向,所以匹配的结果都出现在终端窗口里。
cut -d: -f7 < /etc/passwd | sort -u
这条命令用cut命令从/etc/passwd文件里把每个用户的shell的路径选出来。接着,列出的shell的路径都通过sort-u进行处理,产生的清单中,路径名不但依次排序,且路径名只出现一次。
要让第二条命令只有在第一条命令成功完成之后执行,可以用一个&&符号把两条命令隔开。
cd /usr/local/ && pwd
这条命令进入/usr/local/目录下,然后显示当前路径。这里用一个表示“逻辑与”的符号,那么就可能造成混乱。不要想得太多;仅仅把它当做一个shell的习惯用法就行了。
相反,||这个符号表明,只有前一条命令执行不成功时,才执行后面的命令。
字一个脚本里,可以用反斜线把一条命令分成多行来写,从而把出错处理代码和命令管道的其他部分区分开来;
cp --preserver --recursive /etc/* /spare/backup
|| echo "Did NOT make backup"
要实现相反的效果——将多条命令整合在一行里——可以用分号作为语句分隔符。
将脚本内部的文本块进行重定向
有时候,我们需要对文本块(多行文本)进行重定向,就像对标准输入做的那样。考虑一个特殊情况:源文本就位于shell脚本中。一个实用的例子是向log文件中写入头部数据,可以按照下面的方法完成:
#!/bin/bash
cat<<EOF>log.txt
LOG FILE HEADER
This is a test log file
Function: System statistics
EOF
在cat <
cat log.txt
LOG FILE HEADER
This is a test log file
Function: System statistics