在 Linux 系统里,每个正在运行的程序都会自动打开三个标准数据流,它们是程序与外界通信的基础通道:
- 标准输入(stdin),文件描述符为 0。默认来自键盘输入,程序从这里读取数据。
- 标准输出(stdout),文件描述符为 1。默认输出到终端屏幕,程序把正常结果写到这里。
- 标准错误(stderr),文件描述符为 2。同样默认输出到屏幕,但专门用于显示报错和诊断信息,和标准输出分开。
理解了这三个流向,就能通过“重定向”灵活控制数据的来源和去向。重定向的威力在于:不需要修改程序本身,就可以让同一个命令从文件读取输入、将结果保存到文件、丢弃错误信息,或者把正常输出和错误输出分别处理。
1. 输出重定向:把结果存下来
最常见的需求是将命令的输出保存到文件中,而不是让它一闪而过。重定向符号 > 用于覆盖写入,>> 用于追加写入。
echo "Hello, Linux" > greeting.txt # 把输出写入文件(覆盖原内容)
date >> log.txt # 把当前时间追加到 log.txt 末尾
如果只使用 >,目标文件原本的内容会被完全清空再写入新内容,使用时需格外小心。很多 Shell(如 Bash)提供了 set -o noclobber 来防止意外覆盖已存在的文件,此时追加写入会更安全。
有时会看到类似 > file 的写法,前面没有命令,这会产生一个空文件(或清空已有文件),相当于 touch 的一种简写。
2. 输入重定向:把文件内容喂给命令
很多命令默认从键盘接收输入,但当数据已经保存在文件里时,可以使用 < 让命令直接从文件读取。
wc -l < data.txt # 统计 data.txt 的行数,不显示文件名
sort < unsorted.txt # 对文件内容排序并输出
用输入重定向的好处是命令完全不知道数据的源头是文件还是键盘,因此脚本可以不加修改地处理文件输入。另外,一些命令(如 tr)必须通过输入重定向提供数据,因为它们本身不接受文件名作为参数。
3. 标准错误重定向:分离正常输出和报错
默认情况下,标准输出和标准错误都会打印到屏幕上,混在一起。如果想把它们分开,就需要用到 2> 专门重定向标准错误。
ls /nonexistent_dir > out.txt 2> err.txt
这行命令尝试列出一个不存在的目录。正常输出(本来就没有)会进入 out.txt,而错误信息“No such file or directory”会记入 err.txt。
常见的组合还包括:
command 2>&1:将标准错误合并到标准输出,让它们走同一个流。这对于grep过滤或者一并保存非常有用。
make 2>&1 | tee build.log # 编译并同时看到输出和保存日志
command &> file:将标准输出和标准错误都重定向到同一个文件(相当于> file 2>&1),简洁且不易写错。
4. 管道:将一个命令的输出变成另一个命令的输入
管道符 | 是命令行组合的灵魂。它把前一个命令的标准输出直接连接到后一个命令的标准输入,无需经过中间文件。
ps aux | grep nginx # 从所有进程中筛选包含 nginx 的行
cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head
管道会只传递标准输出,标准错误不受影响,直接输出到终端,这常常是预期行为:你不想让报错信息也混入下游的处理流程。
5. 实用技巧与注意事项
- 避免不必要的 cat:
cat file | grep pattern可以简写为grep pattern file,后者让grep直接打开文件,省去一次进程间通信。 - 临时丢弃输出:
command > /dev/null将标准输出丢入黑洞,常用于脚本中只关心命令是否成功,不关心输出内容。 - 同时写入文件和屏幕:
command | tee file.txt将输出一份保存到文件,另一份继续显示在终端。加上-a选项可以追加写入。 - 重定向顺序很重要:
command 2>&1 > file与command > file 2>&1不一样。前者先把 stderr 复制到当时的 stdout(终端),再把 stdout 改到文件,导致 stderr 仍显示在屏幕上;后者才正确地将两者都写入文件。通常推荐使用&> file避免这个陷阱。
掌握标准流和重定向之后,你就拥有了像搭积木一样构建数据处理流水线的能力。大多数时候,Linux 的高效并非来自记忆一大堆复杂参数,而是来自将这些简单的数据流控制手段灵活应用到实际任务中。