Shell 输入/输出重定向(一文讲透)

Shell 输入/输出重定向:从基础到实战的完整指南

在 Linux 或类 Unix 系统中,Shell 是我们与系统交互的核心工具。而掌握 Shell 输入/输出重定向,是每一位开发者提升效率、编写健壮脚本的必经之路。它看似简单,实则功能强大,能让你灵活控制命令的输入来源和输出去向,甚至实现日志记录、数据流处理、自动化部署等复杂操作。

想象一下:你正在运行一个命令,它本该把结果打印在屏幕上,但你希望把结果保存到文件里,而不是看着它一闪而过。或者,你有一个程序需要从文件读取数据,而不是手动输入。这些场景,正是 Shell 输入/输出重定向的用武之地。

本文将带你从零开始,一步步掌握 Shell 输入/输出重定向的核心概念与实用技巧,结合真实案例,让你真正“用得上、用得好”。


什么是 Shell 输入/输出重定向

在 Shell 中,每个命令默认有三个标准流:

  • 标准输入(stdin):编号为 0,用于接收输入数据,通常来自键盘。
  • 标准输出(stdout):编号为 1,用于输出正常结果,通常显示在屏幕上。
  • 标准错误(stderr):编号为 2,用于输出错误信息,也通常显示在屏幕上。

默认情况下,这些流都连接到终端(Terminal)。但 Shell 允许我们“重定向”它们,即改变它们的来源或目标。

举个例子:

echo "Hello, World!"

这行命令的输出会直接打印在屏幕上,因为 stdout 指向终端。但如果我们想把输出保存到文件,而不是显示,就可以使用重定向。


输出重定向:把结果写入文件

最常用的重定向是输出重定向,它让你可以把命令的 stdout 从屏幕转向文件。

使用 > 重定向覆盖写入

echo "今天天气真好" > weather.txt
  • > 是输出重定向操作符。
  • 如果 weather.txt 不存在,Shell 会自动创建它。
  • 如果文件已存在,>覆盖原有内容。

注释:本命令将字符串 "今天天气真好" 写入 weather.txt 文件,如果文件已存在,原有内容将被完全替换。

使用 >> 追加输出

如果你想在文件末尾添加内容,而不是覆盖,就用 >>

echo "气温25度" >> weather.txt
  • >> 表示“追加”模式。
  • 新内容会被添加到文件最后,原有内容保留。

注释:该命令将 "气温25度" 追加到 weather.txt 文件末尾,适合日志记录、数据累积等场景。

实际案例:日志文件生成

假设你正在运行一个定时任务,想记录每次执行的时间和结果:

echo "[$(date)] 任务执行成功" >> /var/log/mytask.log
  • $(date) 是命令替换,获取当前时间。
  • >> 确保每次执行都追加一条新日志,不会覆盖历史记录。

输入重定向:让命令从文件读取数据

有时我们不想手动输入数据,而是希望命令从文件中读取。这就是输入重定向的用途。

使用 < 重定向输入

sort < names.txt
  • <names.txt 的内容作为 sort 命令的标准输入。
  • sort 会读取文件中的每一行,并按字母顺序排序后输出。

注释:此命令不会修改 names.txt 文件,而是把文件内容“喂”给 sort 命令处理,输出结果仍显示在屏幕上。

实际案例:批量处理用户数据

假设 users.txt 文件包含多行用户名:

alice
bob
charlie
david

你可以用以下命令统计行数:

wc -l < users.txt
  • wc -l 用于统计行数。
  • < users.txt 指定输入源为文件,避免命令提示“请输入数据”。

同时重定向标准输出和标准错误

在实际开发中,程序的输出和错误信息经常需要分别处理。例如,你希望正常输出写入日志文件,错误信息写入另一个文件。

使用 2> 重定向标准错误

ls /tmp /nonexistent 2> error.log
  • 2> 表示重定向标准错误(stderr)。
  • /tmp 存在,输出正常;/nonexistent 不存在,会报错,但错误信息被写入 error.log,而不是显示在屏幕上。

注释:此命令将错误信息(如 "No such file or directory")保存到 error.log,避免污染正常输出。

同时重定向 stdout 和 stderr

如果你想把所有输出(包括错误)都保存到同一个文件:

ls /tmp /nonexistent > all.log 2>&1
  • > all.log 将 stdout 重定向到 all.log。
  • 2>&1 表示将 stderr 重定向到 stdout 的位置,也就是 all.log。

注释:2>&1 是 Shell 中一个经典写法,表示“把 stderr 重定向到 stdout 的目标”。执行后,所有输出都会被写入 all.log。


管道与重定向的组合使用

Shell 的强大之处在于它支持组合操作。管道(|)和重定向可以协同工作,实现复杂的数据处理流程。

案例:查找并过滤日志

假设你有一个日志文件 app.log,想找出所有包含 "ERROR" 的行,并保存到新文件:

grep "ERROR" app.log > error_summary.txt
  • grep "ERROR" 从 app.log 中查找包含 "ERROR" 的行。
  • > 将匹配结果写入 error_summary.txt。

进阶:过滤后排序并去重

grep "ERROR" app.log | sort | uniq > unique_errors.txt
  • grep:筛选关键字。
  • | sort:按字母顺序排序。
  • | uniq:去除重复行。
  • > unique_errors.txt:最终结果保存。

注释:这种链式处理方式,是 Shell 脚本中最常见的数据处理模式,高效且简洁。


重定向的高级技巧:使用文件描述符

Shell 中,每个标准流都有一个编号(0、1、2),这些编号被称为“文件描述符”(file descriptor)。你不仅可以重定向标准流,还可以操作任意文件描述符。

临时创建并使用自定义文件描述符

exec 3> output.log
echo "这是第一条日志" >&3
echo "这是第二条日志" >&3
exec 3>&-
  • exec 3> output.log:打开文件描述符 3 并指向 output.log。
  • >&3:将输出重定向到文件描述符 3。
  • exec 3>&-:关闭文件描述符 3。

注释:这种方式适合在脚本中管理多个输出流,避免污染标准输出。

重定向到同一个文件的多个流

exec > app.log 2>&1
echo "程序开始执行"
ls /tmp /nonexistent
echo "程序结束"
  • exec > app.log:将 stdout 重定向到 app.log。
  • 2>&1:将 stderr 重定向到 stdout 的目标(即 app.log)。
  • 此后所有命令的输出都会被统一记录。

注释:exec 会改变当前 Shell 的默认行为,常用于脚本开头统一设置日志输出。


重定向的常见陷阱与最佳实践

陷阱 1:误用 > 覆盖重要文件

ls > important.txt

如果 important.txt 本来就有内容,会被完全覆盖。建议使用 >> 或先备份。

陷阱 2:忘记转义特殊字符

echo "Hello > world" > output.txt

如果文件名包含 >,Shell 会误认为是重定向操作。建议用引号包裹:

echo "Hello > world" > "output.txt"

最佳实践

  1. 使用 >> 追加而非 >,除非明确需要覆盖。
  2. 日志文件建议使用 .log 扩展名,便于识别。
  3. 在脚本中统一设置重定向,避免混乱。
  4. 使用 exec 一次性设置重定向,提高脚本可读性。

总结:掌握 Shell 输入/输出重定向,提升开发效率

Shell 输入/输出重定向是 Linux 命令行操作的基石。它让你摆脱“只能看屏幕”的限制,能够自由控制数据的流动方向,实现日志记录、自动化处理、错误隔离等高级功能。

从简单的 ><,到 2>&1exec,每一步都为脚本编写和系统管理提供了强大支持。掌握这些技巧,不仅能让你写出更专业的脚本,还能在排查问题、部署服务时事半功倍。

无论你是初学者还是中级开发者,花点时间练习这些命令,把它们融入日常开发流程中,你会发现 Shell 的威力远超想象。