🐧【Linux】Unix管道与重定向
1. 管道 | 和 连字符 -
核心概念:不变的约定
理解后续所有内容的基础,是先掌握两个不变的约定。
1. 管道 |:不变的通用连接器
管道的功能永远是连接左边命令的标准输出 (stdout) 到右边命令的标准输入 (stdin)。它是一个忠实的数据搬运工,本身没有任何逻辑。命令行的行为差异,几乎都源于管道右侧的程序如何处理它的输入。
2. 连字符 -:约定的通用“文件”
单个连字符 - 本身不是 Shell 的一个特殊语法,而是一个被广泛遵守的程序设计约定。当一个程序被设计为需要明确的文件名作为参数时,- 就被用作一个特殊的名字,代表“从标准输入读取”或“向标准输出写入”。
命令设计的两种哲学
理解了上述约定后,我们就能看清 Unix 世界里两种主流的命令设计哲学。
哲学一:过滤器 (Filter) / 流处理器
这是最经典、最符合 Unix “小而美、组合使用”思想的哲学。
- 核心原则:如果命令后面没有提供文件名作为参数,就默认从标准输入(stdin)读取数据,再把结果输出到stdout。
- 设计隐喻:“我是一个数据处理工作站,默认就在原地等待数据从传送带(管道)上流过来。”
- 适用场景:文本搜索 (
grep)、排序 (sort)、计数 (wc)、文本替换 (sed)、数据提取 (awk) 等。 - 管道用法:直接使用
|连接,无需任何额外参数。
过滤器风格,自己知道要去哪里读,哪里写,不需要你告诉它。
# 示例:列出所有文件,并筛选出包含 "test" 的行
ls -l | grep "test"
哲学二:文件操作器 (File Operator) / 显式工具
这类工具通常功能更专一,需要用户明确指定操作对象。
- 核心原则:必须明确地、显式地提供输入和/或输出的文件名。它们不会在缺少文件名参数时默认去读标准输入。
- 设计隐喻:“我是一个专业的工匠,请明确告诉我需要操作哪个‘文件’,以及成品要放在哪里。”
- 适用场景:归档/解压 (
tar)、文件转换 (ffmpeg)、文件拼接 (cat在多参数时)、磁盘镜像 (dd) 等。 - 管道用法:必须使用
-作为“虚拟文件名”来告诉命令从管道读取数据。
# 示例:下载一个压缩包,不保存到本地,直接通过管道解压
# -f - 告诉 tar 要解压的文件来自标准输入
curl http://example.com/archive.tar.gz | tar -xzf -
可复用的经验与最佳实践
1. 如何快速判断一个命令属于哪种哲学?
- 直接尝试:这是最快的方法。
ls | a_command。如果它开始处理数据,它就是“过滤器”;如果它报错“缺少文件参数”或卡住不动,那它很可能是“文件操作器”,需要加上-再试一次 (ls | a_command -)。 - 查阅手册:运行
man a_command。手册里通常会写明。寻找类似 “If no file is specified, standard input is used” 的描述,或者在参数列表里查找对-的专门解释。
2. 当你编写自己的脚本或工具时
- 优先选择“过滤器”哲学:尽可能让你的工具默认从 stdin 读取,向 stdout 写入。这会让你的工具拥有极强的组合能力,能更好地融入命令行生态。
- 如果必须是“文件操作器”,请务必遵守
-约定:在你的参数解析逻辑里,检查文件名是否为-,如果是,就将程序的 I/O 重定向到 stdin/stdout。这会让你的工具成为一个命令行生态里的“好公民”。
3. 发挥组合的力量
真正强大的地方在于组合这两种工具。例如,将一个“文件操作器”的输出,通过管道交给一系列“过滤器”处理,最后再交给另一个“文件操作器”。
# 复杂示例:创建一个目录的压缩包,通过 ssh 传输到远程主机并直接解压
# tar (操作器) -> gzip (过滤器) -> ssh (操作器) -> tar (操作器)
tar -cf - /path/to/local/dir | gzip | ssh user@remote_host "cd /path/to/remote/dir && tar -xzf -"
2. 重定向基础:> » < «
输出重定向 (> 和 >>)
输出重定向控制命令的标准输出 (stdout) 被发送到何处。
1. >:覆盖输出重定向
> 操作符将一个命令的输出发送到一个文件。
- 如果文件已存在,它的所有原始内容都会被覆盖。
- 如果文件不存在,它会自动创建该文件。
示例
# 将当前目录的列表覆盖写入到 file_list.txt
ls -l > file_list.txt
2. >>:追加输出重定向
>> 操作符将一个命令的输出内容追加到一个文件的末尾。
- 它不会覆盖文件原有的内容。
- 如果文件不存在,它同样会自动创建该文件。
示例
# 将当前日期追加到日志文件的末尾
date >> /var/log/app.log
输入重定向 (< 和 <<)
输入重定向控制命令从何处获取其标准输入 (stdin)。
3. <:输入重定向
< 操作符将一个文件的内容用作一个命令的输入。
- 它告诉命令从指定的文件读取数据,而不是从键盘。
示例
# 假设有一个 names.txt 文件,sort 命令会读取其内容并排序输出
sort < names.txt
4. <<:Here Document
<< 操作符是一种特殊的输入重定向,称为 “Here Document”。它允许你将脚本中的多行文本直接作为命令的输入。
<<后跟一个自定义的分隔符(如EOF),Shell 会读取其后的所有文本,直到遇到一个只包含该分隔符的独立行。
示例
# 使用 cat 命令和 Here Document 创建一个包含多行内容的文本文件
cat << EOF > greeting.txt
Hello, World!
This is a multi-line text file.
EOF
快速参考
| 操作符 | 名称 | 方向 | 作用 |
|---|---|---|---|
> |
覆盖输出重定向 | 命令 -> 文件 |
将命令的输出覆盖写入到文件。 |
>> |
追加输出重定向 | 命令 -> 文件 |
将命令的输出追加到文件末尾。 |
< |
输入重定向 | 文件 -> 命令 |
将文件内容作为命令的输入。 |
<< |
Here Document | 文本块 -> 命令 |
将脚本内文本块作为命令的输入。 |
相关笔记
- Linux的进程、线程、文件描述符是什么
- 让linux终端效率倍增的工具