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终端效率倍增的工具