Linux: 标准流与重定向——stdin / stdout / stderr
Linux 把每个程序都看作一个"过滤器"——从某处接收输入、处理后输出结果。输入和输出的通道就是标准流。
📋 前置知识:需要先掌握以下内容
- 第5课:文件与目录操作
1. 你将学到
- 标准输入/输出/错误的概念
>和>>的区别2>重定向错误&>同时重定向/dev/null的应用
2. 一个满屏报错的故事
(1) 痛点:命令输出混在一起,找不到想要的信息
小明运行 apt update,成功的信息和红色的错误信息混在一起满屏滚动。他只想把成功的结果存起来,把错误单独看。
(2) 用重定向分开输出
Bob 教他用重定向:
BASH
# 成功信息存到 success.log,错误信息存到 error.log
sudo apt update > success.log 2> error.log
# 查看错误
cat error.log
(3) 收益:精准控制输出
小明学会了将不同输出分流——日志文件只记录成功的,终端只显示重要的,错误单独保存方便排查。
3. 知识点讲解
(1) 三大标准流
Linux 程序启动时自动获得三个文件描述符:
| 文件描述符 | 名称 | 默认连接 | 用途 |
|---|---|---|---|
| 0 | stdin | 键盘 | 程序的输入 |
| 1 | stdout | 终端屏幕 | 程序的正常输出 |
| 2 | stderr | 终端屏幕 | 程序的错误输出 |
TEXT
📖 仅展示
┌──────────┐
stdin ──→│ │──→ stdout
(键盘) │ 程序 │ (屏幕)
│ │──→ stderr
└──────────┘ (屏幕)
(2) 重定向操作符
| 操作符 | 含义 | 示例 |
|---|---|---|
> |
将 stdout 写入文件(覆盖) | ls > files.txt |
>> |
将 stdout 追加到文件 | echo "done" >> log.txt |
< |
从文件读取 stdin | sort < list.txt |
2> |
将 stderr 写入文件 | cmd 2> error.log |
2>> |
追加 stderr 到文件 | cmd 2>> error.log |
&> |
将 stdout 和 stderr 都写入文件 | cmd &> all.log |
&>> |
追加 stdout 和 stderr 到文件 | cmd &>> all.log |
2>&1 |
将 stderr 合并到 stdout | cmd > log.txt 2>&1 |
ℹ️ 说明:
>是覆盖写入(文件原有内容会被清空),>>是追加写入(在文件末尾添加)。需要保存历史日志时务必用>>;需要干净的状态时用>。误用>会覆盖重要日志文件。
(3) /dev/null——黑魔法
/dev/null 是一个特殊的设备文件——写入它的任何数据都会被丢弃,读取它会返回空。
💡 提示:
/dev/null 是 Linux 的"黑洞设备"——内核在驱动层面直接丢弃写入的数据,不占用磁盘空间,永远不会满。常用于静默命令输出:command &> /dev/null 完全不显示任何输出,command 2> /dev/null 只屏蔽错误。
TEXT
📖 仅展示
# 忽略所有输出
command > /dev/null 2>&1
# 或者更简洁的写法
command &> /dev/null
# 只忽略错误
command 2> /dev/null
# 只忽略正常输出
command > /dev/null
▶ 示例:保存命令输出到文件
BASH
# 覆盖写入
ls ~ > home-directory.txt
cat home-directory.txt
# 追加写入
echo "新的一行" >> home-directory.txt
echo "又来一行" >> home-directory.txt
cat home-directory.txt
输出:
TEXT 📖 仅展示Desktop Documents
Downloads
新的一行 又来一行
▶ 示例:分离正常输出和错误输出
BASH
# 成功信息保存,错误信息单独看
find /etc -name "*.conf" > found.txt 2> errors.txt
# 查看结果
echo "=== 找到的配置文件 ==="
head -5 found.txt
echo "=== 错误信息 ==="
cat errors.txt
输出:
TEXT 📖 仅展示=== 找到的配置文件 === /etc/adduser.conf /etc/ca-certificates.conf /etc/debconf.conf /etc/deluser.conf /etc/fuse.conf === 错误信息 === find: '/etc/ssl/private': Permission denied
▶ 示例:合并输出
BASH
# 方式一:&>(bash 4+)
grep -r "error" /var/log/ &> all-results.txt
# 方式二:2>&1(更通用)
grep -r "error" /var/log/ > all-results.txt 2>&1
# 两种方式效果一样——stdout 和 stderr 都存入同一个文件
> 💡 **提示**:`2>&1` 的顺序很重要!`cmd > log.txt 2>&1` 是正确的——先将 stdout 重定向到文件,再把 stderr 合并到 stdout(此时 stdout 已经指向文件)。如果写成 `cmd 2>&1 > log.txt`,stderr 会先合并到屏幕上的 stdout,然后 stdout 才重定向到文件,stderr 仍然输出到屏幕。
输出:
TEXT 📖 仅展示$ wc -l all-results.txt 42 all-results.txt
▶ 示例:/dev/null 屏蔽输出
BASH
# 完全静默运行
sudo apt update > /dev/null 2>&1
# 简洁写法
sudo apt update &> /dev/null
# 只屏蔽错误
find / -name "*.py" 2> /dev/null
输出:
TEXT 📖 仅展示(无输出——所有错误和正常结果均被屏蔽)
▶ 示例:heredoc——在脚本中嵌入多行文本
BASH
# 创建多行配置文件
cat << EOF > nginx.conf
server {
listen 80;
server_name example.com;
root /var/www/html;
}
EOF
# 追加多行文本
cat << EOF >> ~/.bashrc
# 添加 PATH
export PATH="\$PATH:\$HOME/scripts"
EOF
# heredoc 中的变量不会被展开(用 'EOF' 加引号)
cat << 'EOF' > greeting.sh
#!/bin/bash
echo "Hello, $USER" # $USER 保留为变量名
EOF
输出:
TEXT 📖 仅展示server { listen 80; server_name example.com; root /var/www/html; }
▶ 综合示例:编译程序时的输出管理
BASH
# 编译 C 程序,分别记录输出和错误
gcc main.c -o app \
> build.log \ # 编译成功信息
2> build-errors.log # 编译错误信息
# 如果编译成功,build-errors.log 是空的
# 如果编译失败,查看错误
if [ -s build-errors.log ]; then
echo "❌ 编译失败,错误信息:"
cat build-errors.log
else
echo "✅ 编译成功!"
fi
❓ 常见问题
Q:
>和|(管道)有什么区别? A: \1
Q:
2>&1是什么意思? A: \1
Q: 为什么用
&>不用2>&1? A:&>是 bash 4+ 的语法糖,更简洁。2>&1更通用(在 sh 中也能用)。两者功能相同。
Q:
/dev/null是真空吗?会满吗? A: 不会。写入/dev/null的数据被内核直接丢弃,不占用任何磁盘空间。可以说是"无限容量的黑洞"。
Q: heredoc 里的
EOF必须大写吗? A: 不必须。EOF只是惯例,可以用任何字符串(EOL、END、__EOF__)。关键是开始和结束的标记必须完全一致。
📖 小节
- 标准输入(stdin/0)→ 程序 → 标准输出(stdout/1)+ 标准错误(stderr/2)
>覆盖写入、>>追加写入、2>重定向错误&>合并输出,/dev/null丢弃输出2>&1将 stderr 合并到 stdout(注意顺序)- heredoc
<< EOF ... EOF在脚本中嵌入多行文本
📝 作业
- 基础题(难度⭐):用
ls -la将输出保存到home-list.txt,然后用>>追加当前日期 - 进阶题(难度⭐⭐):运行
find / -name "*.py" 2> errors.txt查看权限拒绝错误,然后用&>同时捕获 stdout 和 stderr 到同一个文件,用/dev/null屏蔽错误输出 - 挑战题(难度⭐⭐⭐):用 heredoc 创建一个包含 5 行内容的配置文件,然后写一个脚本分别用
>、2>、&>和>>演示四种重定向方式,并解释每种场景的适用情况