探秘 Shell 重定向:让你的命令行“指哪打哪”

你以为命令行只是敲敲命令?错!学会了重定向,你就能让数据像听话的小狗一样,想去哪就去哪。


前言:命令行的“水管工”必修课

如果你刚接触 Linux 或 macOS 的终端,可能会觉得命令行像个“黑盒子”——输入一条命令,屏幕就“吐”出一堆文字。但你有没有想过:

  • 怎么把命令的输出保存到文件里,而不是只显示在屏幕上?
  • 怎么把文件内容喂给命令当输入?
  • 怎么把报错信息单独拎出来,不让它和正常输出混在一起?

答案就是 —— Shell 重定向。说白了,它就是命令行的“水管工”,负责把数据的源头去处重新接一接。

准备好了吗?让我们撸起袖子,开始这场“水管改造”之旅。


一、先搞懂三个“水管口”

每个 Linux 命令启动时,系统都会给它配好三根默认的“水管”:

水管名字 编号(文件描述符) 默认去向 通俗比喻
标准输入 0 键盘 水龙头(进水)
标准输出 1 屏幕 出水管(正常水)
标准错误 2 屏幕 出水管(污水)

💡 小知识:为什么错误和输出默认都往屏幕跑?因为设计者觉得“正常消息”和“错误消息”你都应该立刻看到。但你可以把它们分开,后面会讲。


二、基础重定向:三招玩转 > 和 >>

1. 输出重定向 > —— “覆盖式倒水”

把命令的标准输出倒进文件,屏幕就看不到了。

# 把当前目录列表写入 list.txt(如果文件存在,会覆盖掉原来的内容)
ls -l > list.txt

# 再看看屏幕上还有输出吗?没有,全跑文件里了
cat list.txt

注意>覆盖模式,相当于“清空文件再写”。小心别把重要文件覆盖了!

2. 追加重定向 >> —— “接水式追加”

想保留文件原有内容,只在末尾追加?用 >>

# 先写第一行
echo "第一条记录" > diary.txt
# 再追加一行
echo "第二条记录" >> diary.txt

cat diary.txt
# 输出:
# 第一条记录
# 第二条记录

3. 错误重定向 2> —— “单独收集污水”

默认错误和输出都显示在屏幕上,但你可以把错误单独存下来。

# 找一个不存在的文件,会产生错误
ls not_exist.txt 2> error.log

# 屏幕上干干净净,错误全在 error.log 里
cat error.log
# 输出类似:ls: cannot access 'not_exist.txt': No such file or directory

🧙‍♂️ 魔法编号2> 中的 2 就是文件描述符(标准错误)。1> 可以省略 1,因为 > 默认就是标准输出。

4. 同时重定向输出和错误 —— “分流还是合流?”

方法一:分两个文件存

command > output.log 2> error.log

方法二:都扔进同一个文件(合流)

  • 老式写法(兼容所有 shell):

    command > all.log 2>&1

    解释:先把标准输出指向 all.log,然后 2>&1 表示“让标准错误也去标准输出当前去的地方”。

  • 现代简写(bash / zsh 支持):

    command &> all.log
    # 或者
    command >& all.log

🍺 喝啤酒的比喻:2>&1 就像你跟朋友说“他的杯子空了,你就把你的酒倒给他”——错误输出跟着输出走。


三、输入重定向 < —— “让命令从文件里吃饭”

默认命令从键盘读取输入,但你可以用 < 让命令从文件读取。

# 普通用法:wc 统计文件单词数
wc -w < story.txt

# 给 sort 命令喂一个文件,它排序后输出
sort < names.txt

也可以和输出重定向连用,形成管道线的替代方案:

# 从 input.txt 读,排序后写到 output.txt
sort < input.txt > output.txt

四、终极合体技:管道 | —— “命令接龙”

重定向的集大成者其实是管道。它不经过中间文件,直接把左边命令的输出“喂”给右边命令作为输入。

# 查看历史命令中 grep 用了多少次
history | grep "grep" | wc -l

# 找到最耗 CPU 的前 5 个进程
ps aux | sort -rnk 3 | head -5

🎢 管道的乐趣就像游乐园的滑水道——数据从上一个滑梯滑进下一个,一滴水都不浪费。

管道 vs 重定向:一张表看懂

| 特点 | 管道 | | 重定向 > / < |
| :— | :— | :— |
| 连接对象 | 命令 ↔ 命令 | 命令 ↔ 文件 |
| 是否存盘 | 不存盘,内存传递 | 最终写入文件 |
| 典型场景 | 数据流式处理 | 保存结果 / 读取配置 |


五、进阶技巧:让你的命令行“开挂”

1. tee 命令 —— “分流兼偷看”

有时候你想既把输出存到文件,又能在屏幕上看到,怎么办?tee 就是你的“复制水阀”。

# 运行命令,屏幕实时显示,同时写入 result.log
ls -l | tee result.log

# 追加模式(-a 相当于 append)
echo "新的内容" | tee -a result.log

2. Here Document —— “直接在脚本里写多行输入”

在 shell 脚本中,你想给命令提供多行输入,但又不想单独建个文件。用 << 加一个分界符

cat << EOF > hello.txt
这是第一行
这是第二行
$HOME 变量会被展开哦
EOF

如果不想展开变量(比如 $HOME 原样输出),把分界符用引号包起来:

cat << 'EOF' > raw.txt
$HOME 不会被展开,原样写入
EOF

3. Here String —— “单行输入快捷方式”

<<< 可以把一个字符串当作标准输入送给命令。

# 统计单词数
wc -w <<< "Hello world, this is a test"
# 输出:5

# 将字符串转为大写
tr 'a-z' 'A-Z' <<< "shell is fun"
# 输出:SHELL IS FUN

4. 重定向到 /dev/null —— “扔进黑洞”

不想看到任何输出(包括错误)?扔进 /dev/null,这个“系统黑洞”会吞掉一切。

# 安静地执行命令,不产生任何屏幕输出
command > /dev/null 2>&1

# 更简单的现代写法
command &> /dev/null

常用于后台脚本或测试环境。


六、实战小剧场:三个真实场景

场景一:编译软件时只保存错误

make 1> build_output.log 2> build_error.log

正常编译日志进 build_output.log,只有警告和错误进 build_error.log。排查问题超方便。

场景二:自动备份脚本,并记录时间戳

#!/bin/bash
{
echo "=== Backup started at $(date) ==="
rsync -av /home/user/data/ /backup/data/
echo "=== Backup finished at $(date) ==="
} >> backup.log 2>&1

{ ... } 把多行命令的输出一起重定向,干净利落。

场景三:交互式程序自动化(比如 ftp)

ftp -n << EOF
open ftp.example.com
user anonymous pass@
get readme.txt
bye
EOF

把所有 ftp 命令通过 Here Document 喂进去,完成自动下载。


七、避坑指南(老司机血泪经验)

❌ 错误写法 后果 ✅ 正确写法
command > file 2>file 两个流争抢写同一个文件,可能乱码或丢失 command &> filecommand > file 2>&1
command 2>&1 > file 2>&1 在前时,标准错误还指向屏幕(因为当时标准输出还在屏幕) command > file 2>&1 (顺序调换)
echo "text" > /proc/somefile 特殊文件(如 /proc 下)不能随意覆盖 确认文件是普通文件再重定向
cat file > file 清空 file 后再读它?文件先被截断,啥也读不到 用临时文件或 sponge(`cat file

🚨 记住:重定向的解析顺序是从左到右,2>&1 必须在 > file 之后才能让错误也进文件。


八、彩蛋:一个“无用但有趣”的重定向

# 把自己写的代码打印出来,再编译运行,再输出……(别在生产环境玩)
cat self_print.sh | bash

或者玩一把“读自己的源代码”:

# 一个自指的命令
echo 'cat < this_script.sh' > this_script.sh
bash this_script.sh # 输出 it 自身内容

总结:重定向三板斧

  1. 输出> 覆盖,>> 追加,2> 错误单独存。
  2. 输入< 从文件读,<< Here Document 多行输入,<<< Here String 单行。
  3. 合体| 管道连接命令,tee 分流显示,&> 合并输出与错误。

掌握了重定向,你的命令行就不再是单向的“广播”,而变成了可以任意编排的“数据乐高”。不管是写脚本、分析日志,还是日常骚操作,都能指哪打哪,得心应手


📖 延伸阅读man bash 搜索 “REDIRECTION”,或者看看 info coreutils 'Standard I/O'
💬 喜欢这篇文章?欢迎在评论区分享你遇到过的“重定向翻车现场”!

Happy Hacking! 🐧