三大标准流 / 重定向全家桶 / 管道 / tee / xargs · 拆分自 Linux01 命令系统实战笔记
六、重定向与管道
三大标准流(必须记住)
| 名称 | 文件描述符 fd | 默认指向 |
|---|---|---|
| 标准输入 stdin | 0 | 键盘 |
| 标准输出 stdout | 1 | 终端屏幕 |
| 标准错误 stderr | 2 | 终端屏幕 |
重定向 = 修改这三个流的指向。
1. 输出重定向:> 和 >>
command > file # stdout 覆盖写入 file(清空原有内容)
command >> file # stdout 追加写入 file 末尾
⚠️ 核心:> 和 >> 默认只重定向 stdout!stderr 仍然打印屏幕。
ls /notexist > out.log
# out.log 是空的(因为 ls 没有正常输出)
# 错误信息 "No such file or directory" 直接打印在屏幕上
2. 标准错误重定向:2> 和 2>>
ls /notexist 2> err.log # stderr 覆盖写入 err.log
ls /notexist 2>> err.log # stderr 追加写入 err.log
3. stdout + stderr 一起重定向
# 写法1(通用):stdout 到文件,stderr 重定向到 stdout 的目标
ls /etc /notexist > all.log 2>&1
# 写法2(bash 特有简写)
ls /etc /notexist &> all.log
🔑 面试必考:2>&1 顺序不能写反!
# ❌ 错误:先 2>&1(此时 stdout 还指向屏幕),再 > all.log
ls /etc /notexist 2>&1 > all.log
# 结果:stdout 进了 all.log,但 stderr 还是打印屏幕
# ✅ 正确:先 > all.log(stdout 指向文件),再 2>&1(stderr 跟着 stdout 走)
ls /etc /notexist > all.log 2>&1
理解关键:
2>&1是"把 stderr 重定向到 stdout 当前指向的地方",不是"把 stderr 和 stdout 绑在一起"。所以顺序很重要。
4. 输入重定向:<
wc -l < test.txt # 把 test.txt 内容作为 wc 的 stdin
很多命令支持直接传文件名(wc -l test.txt),< 更多用于脚本和 here-doc。
5. Here-doc:<< 多行输入
cat << EOF > msg.txt
第一行文字
第二行文字
第三行文字
EOF
EOF是自定义分界符(也可以写成其他词)- 遇到单独一行的
EOF就结束输入 - 常用于脚本里批量写配置文件
⚠️ 易错点:重定向符号 > msg.txt 要放在 cat 后面,不能放在 EOF 后面!
6. 管道:|
命令1 | 命令2 | 命令3
核心:把命令 1 的 stdout 作为命令 2 的 stdin。
⚠️ 关键认知:
1. 管道只传递 stdout,不传递 stderr!stderr 直接穿过管道打印屏幕。
2. 管道是内存数据流,不生成中间文件。
3. 管道每一段命令都在独立子 shell 中执行。
ps aux | grep ssh
cat /etc/passwd | wc -l
ls -l /etc | less
|&(拓展):stdout + stderr 一起送入管道,等价于 2>&1 |
ls /etc /不存在 |& grep "No such"
7. tee —— 管道分支(一边写文件一边输出屏幕)
ls -l /etc | tee list.txt # 结果存 list.txt,同时屏幕显示
ls /home | tee -a list.txt # -a:追加写入
典型场景:运行一个长时间命令,既想看实时输出,又想存日志。
重定向符号速查表
| 符号 | 作用 |
|---|---|
> |
stdout 覆盖写入文件 |
>> |
stdout 追加写入文件 |
2> |
stderr 覆盖写入 |
2>> |
stderr 追加写入 |
&> |
stdout + stderr 覆盖写入 |
&>> |
stdout + stderr 追加写入 |
< |
文件作为 stdin 输入 |
<<EOF |
here-doc 多行输入 |
\| |
管道:前 stdout 作为后 stdin |
\|& |
stdout + stderr 送入管道 |
七、xargs(管道黄金搭档)
核心一句话
管道 | 把内容作为 stdin 传给后一个命令;
xargs 把 stdin 里的文本拆分成【命令参数】。
很多命令(
rm、cp、mv、mkdir)不支持从 stdin 读数据,必须靠 xargs 转成参数。
而cat、grep、wc这类命令可以直接读 stdin,不需要 xargs。
基础示例
文件 name.txt:
file1.txt
file2.txt
file3.txt
cat name.txt | xargs touch
# 等价执行:touch file1.txt file2.txt file3.txt
为什么不能直接 cat name.txt | touch?因为 touch 不读 stdin!
常用参数
-n 数字:每次传几个参数
cat name.txt | xargs -n1 touch
# 等价:touch file1.txt
# touch file2.txt
# touch file3.txt
🔑 面试考点:-n1 vs 不带 -n 的区别
- 不带 -n:一次性传所有参数,只执行一次命令(效率高,少创建进程)
- -n1:每次只传 1 个参数,执行多次命令(多次创建进程,开销大)
- ⚠️ -n1 不改变分割规则!xargs 默认仍然按空格、Tab、换行分割。
-I {}:占位符(高频!)
{} 代表读到的内容,可以放在命令任意位置。
ls *.txt | xargs -I {} cp {} {}.bak
# 一次处理一行,等价:cp a.txt a.txt.bak
# cp b.txt b.txt.bak
⚠️ 注意:-I 是大写 i!不是小写 L。-I 隐含了 -n1。
-d:指定分隔符
默认按空白(空格/Tab/换行)分割。
cat list.txt | xargs -d '\n' rm # 只用换行做分隔
-0:配合 find 处理带空格文件名(生产最常用)
find . -name "*.log" -print0 | xargs -0 rm
find -print0:输出文件名用\0(空字符)分隔xargs -0:用\0分割- 完美解决文件名包含空格、换行、特殊字符的问题
-r:输入为空时不执行命令
find . -name "*.tmp" -print0 | xargs -0 -r rm
- 不加
-r:find 没找到文件 → xargs 执行裸rm→ 报rm: missing operand - 加
-r:输入为空就不执行命令 - ⚠️
-r是 GNU xargs(Linux)特有,macOS/BSD 没有这个参数。
评论