39·Shell 基础进阶

输入输出重定向

redirectionstdoutstderr

第39课:输入输出重定向

学习目标

  1. 理解 Linux 中标准输入(stdin)、标准输出(stdout)和标准错误(stderr)的概念及其文件描述符。
  2. 掌握使用重定向操作符(>, >>, 2>, &> 等)来控制命令的输入和输出流向。
  3. 学会将命令的正确输出和错误输出分别或合并重定向到文件。
  4. 能够应用输入重定向(<)和 “Here Document” (<<) 将文件或文本块作为命令的输入。
  5. 通过组合重定向解决实际运维任务,如日志记录、任务调度和批量处理。

核心概念

在 Linux 的世界里,一切都围绕着“文件”展开。当一个程序(比如一个 Shell 命令)运行时,系统会默认为它打开三个“文件流”(通道),这三个通道都有一个对应的数字标签,称为文件描述符

  • 标准输入 (Standard Input, stdin): 文件描述符 0。这是程序默认的信息来源,通常是你的键盘输入。
  • 标准输出 (Standard Output, stdout): 文件描述符 1。这是程序默认的正常结果输出地,通常是你的终端屏幕。
  • 标准错误 (Standard Error, stderr): 文件描述符 2。这是程序默认的错误信息输出地,通常也是你的终端屏幕。

重定向 (Redirection) 的核心思想,就是改变这些数据的默认流向。想象一下,你有一条溪流(stdout)和一条泥水沟(stderr),它们本来都流进同一个湖泊(终端屏幕)。通过重定向,你可以决定让清水流进一个水缸(文件A),泥水流进另一个水缸(文件B),或者全部汇入一个大水库(文件C)。

常用的重定向操作符:

  • > : 将标准输出 (stdout) 重定向到文件(覆盖原有内容)。
  • >> : 将 stdout 重定向到文件(追加到文件末尾)。
  • 2> : 将标准错误 (stderr) 重定向到文件(覆盖)。
  • 2>> : 将 stderr 重定向到文件(追加)。
  • &>2>&1 : 将标准输出和标准错误合并重定向到同一个文件(覆盖)。2>&1 的意思是“将描述符2(stderr)重定向到描述符1(stdout)当前所指向的地方”。
  • < : 将文件内容作为命令的标准输入 (stdin)

下面这个流程图清晰地展示了数据在重定向下的流向:

flowchart TD
    A[命令执行] --> B{输出类型?}
    
    B --> C[正常输出<br>Stdout (fd 1)]
    B --> D[错误输出<br>Stderr (fd 2)]
    
    C --> E{重定向?}
    D --> F{重定向?}
    
    E -- “使用 >” --> G[覆盖写入文件]
    E -- “使用 >>” --> H[追加写入文件]
    E -- “未重定向” --> I[输出到终端屏幕]
    
    F -- “使用 2>” --> G
    F -- “使用 2>>” --> H
    F -- “未重定向” --> I
    
    I --> J[用户在屏幕看到结果]
    G & H --> K[数据保存在文件中]

代码示例

1. 将命令输出保存到文件(覆盖)

# 将当前目录的文件列表写入 `filelist.txt`
# 如果文件已存在,原有内容会被清空。
ls -l > filelist.txt
cat filelist.txt # 查看文件内容

2. 将命令输出追加到文件

# 记录当前时间到日志文件
date >> my_app.log
# 再记录一条,会追加到 `date` 输出的后面
echo "系统启动完成" >> my_app.log
cat my_app.log

3. 将错误输出与正常输出分离

# 尝试列出一个不存在的文件。`ls` 会正常工作,但会报错。
# `success.log` 将只包含正常的文件列表(如果有的话)。
# `error.log` 将包含错误信息。
ls -l existing_file.txt non_existing_file.txt 1> success.log 2> error.log
echo "--- 成功输出 ---"
cat success.log
echo "--- 错误输出 ---"
cat error.log

4. 合并所有输出到一个文件

# 运行一个可能产生混合输出的复杂命令
# 方式一:使用 `&>` (Bash 简写)
find / -name "*.conf" -type f 2>&1 | head -n 10 > find_result.log 2>&1
# 方式二:使用 `2>&1` (更通用)
./my_script.sh > all_output.log 2>&1
# 解释:先将 stdout 重定向到文件,然后将 stderr 重定向到 stdout 当前指向的文件。

5. 输入重定向与 Here Document

# 使用 `<` 将文件内容作为命令的输入
wc -l < /etc/passwd # 统计文件行数,`wc` 不显示文件名,只显示数字。

# 使用 “Here Document” (<<) 提供多行输入
# 这常用于脚本中向另一个程序(如 `cat` 或交互式程序)传递多行文本。
cat << EOF > config.txt
This is line 1.
This is line 2.
Setting=value
EOF
# 上面的命令创建了一个 `config.txt` 文件,内容是两个 `EOF` 之间的所有文本。

实践练习

练习1:基础输出重定向hostname 命令和 uptime 命令的输出,分别追加到同一个文件 system_status.log 中。

  • 要求:一次追加,不覆盖前一个命令的结果。
  • 预期文件内容:第一行是主机名,第二行是系统运行时间和负载。

练习2:错误与正常输出分离 尝试用 cat 命令同时读取一个存在的文件 /etc/hostname 和一个不存在的文件 /etc/not_a_real_file

  • 要求
    1. 将正常内容(来自存在的文件)重定向到 good.txt
    2. 将错误信息重定向到 bad.txt
  • 预期结果good.txt 包含主机名,bad.txt 包含 “No such file or directory” 类型的错误。

练习3:综合应用(挑战题) 编写一个一行命令,该命令将:

  1. 执行 find 命令在 /tmp 目录下查找所有类型为普通文件的文件。
  2. 抑制掉所有权限不足等可能导致的错误信息(即让 stderr 消失)。
  3. 只将找到的文件列表的前5行保存到文件 /tmp/top5_files.txt
  • 提示:需要组合 find, 重定向, 和管道 | (下节课详解) 的知识。思考如何让 stderr 到“黑洞” (/dev/null)。

常见错误

  1. 混淆 >>>:使用 >覆盖文件内容。如果只想追加日志,误用 > 会丢失历史记录。这是非常严重的错误。
  2. 忘记重定向 stderr:很多命令的错误信息是很有价值的(如调试脚本)。默认情况下,错误信息仍会打印在屏幕上。如果你将 stdout 重定向到了文件,但忘了处理 stderr,文件里可能只有一部分信息,而屏幕被错误信息刷屏。
  3. 顺序错误导致的困惑:命令 ls > file.txt 2>&1ls 2>&1 > file.txt 结果不同
    • 第一个:先让 stdout 指向 file.txt,然后让 stderr 也指向 file.txt(同一个地方)。正确
    • 第二个:先让 stderr 指向当前的 stdout(即屏幕),然后让 stdout 指向 file.txt。结果 stderr 仍在屏幕,stdout 在文件。通常不是你想要的
  4. 误解 /dev/null:它是一个特殊的“黑洞”文件,任何写入它的数据都会被丢弃。常用来丢弃不需要的输出,例如 command > /dev/null 会丢弃所有正常输出,command 2> /dev/null 会丢弃所有错误信息。

小结

  • 三个流:程序默认有 stdin (0), stdout (1), stderr (2) 三个数据流。
  • 重定向本质:改变这些数据流的去向,使其流向文件或来自文件,而不是默认的屏幕和键盘。
  • 关键操作符
    • >, >> 控制 stdout 的覆盖/追加。
    • 2>, 2>> 控制 stderr
    • &>> file 2>&1 用于合并 stdoutstderr
    • < 用于输入重定向。
  • 实践意义:掌握重定向是编写健壮 Shell 脚本和进行高效系统管理的基础。它用于日志收集、批处理任务输出管理、以及抑制无关的错误信息。

本课为你装备了精细控制程序输入输出的能力。在下一课中,我们将学习管道 (|),它是一种更强大的工具,能将一个命令的 stdout 无缝地连接到另一个命令的 stdin,从而构建出功能强大的命令处理链。

练习编辑器

rust
Loading...

继续学习

完成本课后,建议继续学习下一课「管道与命令组合」 以巩固所学知识。