07 管道重定向与命令组合

学习目标

  • 能把一个命令的输出交给另一个命令。
  • 能把输出保存到文件。
  • 能根据前一个命令成功或失败决定是否继续执行。
  • 能区分 stdin、stdout、stderr 与文件描述符,并验证 pipeline 中间失败。

命令总览

命令用途含义基本用法例子
|管道把左侧命令的标准输出传给右侧命令命令1 | 命令2ls -lah | grep -F '.log'
>覆盖写入文件将标准输出写入文件,原内容会被覆盖命令 > 文件echo hello > a.txt
>>追加写入文件将标准输出追加到文件末尾命令 >> 文件echo world >> a.txt
<从文件读取输入把文件内容作为命令输入命令 < 文件sort < names.txt
2>重定向错误输出把标准错误写入文件命令 2> 文件find . -name a 2> errors.txt
&&成功后执行左侧命令成功才执行右侧命令命令1 && 命令2mkdir test && cd test
||失败后执行左侧命令失败才执行右侧命令命令1 || 命令2cd project || echo "目录不存在"
tee同时显示并写文件复制 stdin 到 stdout 和文件命令 | tee 文件printf '%s\n' ok | tee result.txt

常用场景

筛选命令输出:

ls -lah -- . | grep -F '.log'
pgrep -a -u "$(id -u)" -f 'python3'

第一条只演示文本过滤;进程查找使用 pgrep,避免 ps | grep 把 grep 自己的命令行误判为目标进程。

保存输出到文件:

echo "hello" > message.txt
echo "world" >> message.txt

把错误输出单独保存:

find . -name '*.conf' > find_results.txt 2> find_errors.txt

只在成功后继续:

mkdir project && cd project

失败时给出提示:

cd project || echo "project 目录不存在"

新手注意

  • > 会覆盖文件,想保留原内容就用 >>。
  • 管道 | 传递的是输出文本,不是文件本身。
  • 2> 只重定向错误输出,不影响正常输出。
  • && 常用于防止前一步失败后继续执行后续命令。
  • 命令组合很强大,删除或覆盖文件前要先用无破坏命令预览结果。

三个标准流与重定向顺序

Linux 进程通常从文件描述符 0 读 stdin,向 1 写 stdout,向 2 写 stderr。重定向从左到右处理:

command_that_may_fail > combined.log 2>&1
command_that_may_fail 2>&1 > stdout-only.log

两条不等价。第一条让 fd 2 跟随已经指向文件的 fd 1;第二条先让 fd 2 指向原终端,再单独移动 fd 1。示例命令名只是签名说明,不要原样执行。

重定向会先截断

sort data.txt > data.txt 可能在 sort 读取前把输入文件截断为空。输出必须写到新文件,校验后再受控替换。

Pipeline 的退出状态

Bash 默认用最后一条命令的状态作为整条 pipeline 状态:

false | true
pipeline_rc=$?
printf 'pipeline_rc=%s\n' "$pipeline_rc"

$? 的赋值也会覆盖 PIPESTATUS,所以查看分段结果时要重新运行并立即保存:

false | true
parts=("${PIPESTATUS[@]}")
printf 'left=%s right=%s\n' "${parts[0]}" "${parts[1]}"

开启 Bash pipefail 后,pipeline 返回最右侧非零命令的状态;全部成功才为 0:

set -o pipefail
false | true
pipeline_rc=$?
printf 'pipeline_rc=%s\n' "$pipeline_rc"

PIPESTATUS 与 pipefail 是 Bash 能力,不是所有 POSIX Shell 都有。脚本也不能只靠 set -e 代替明确错误处理,因为它在条件、子 Shell 和组合命令中的行为有语境差异。

受控练习

在实验目录创建 input.txt,分别把 stdout、stderr 和合并输出写入三个新文件;再构造 grep 'missing' input.txt | wc -l,比较默认状态、${PIPESTATUS[@]} 和 pipefail。报告中说明“0 行”为什么不能证明 grep 成功。

参考资料