Shell脚本如何实现输入输出重定向

wen 实用脚本 1

Shell脚本如何实现输入输出重定向:从基础到高级实战指南

📑 目录导读

  1. 什么是输入输出重定向? —— 理解核心概念
  2. 基础重定向操作符 —— ><>> 等详解
  3. 文件描述符与重定向的关系 —— stdin、stdout、stderr
  4. 标准错误与标准输出的分离与合并 —— 2>&1 与 &>
  5. Here Document与Here String —— 内联输入重定向
  6. exec命令的高级重定向技巧 —— 永久改变I/O
  7. 使用tee命令同时输出到屏幕和文件
  8. 实战案例:日志收集与错误处理脚本
  9. 常见问题与QA问答

什么是输入输出重定向?

在Linux/Unix系统中,每个Shell脚本默认从标准输入(stdin)读取数据,并将结果输出到标准输出(stdout),错误信息输出到标准错误(stderr),输入输出重定向允许你改变这些数据流的方向,

Shell脚本如何实现输入输出重定向

  • 将命令的输出写入文件(而非屏幕)
  • 从文件读取输入(而非键盘)
  • 将错误信息单独保存

这种机制是Shell脚本自动化处理数据、日志记录和错误隔离的基石。


基础重定向操作符

操作符 功能 示例
> 将stdout覆盖写入文件 ls > filelist.txt
>> 将stdout追加到文件 echo "new line" >> log.txt
< 从文件读取stdin sort < unsorted.txt
2> 将stderr写入文件 cmd 2> errors.log
&> 将stdout和stderr合并写入 cmd &> all.log

示例:

# 将当前目录列表写入文件(覆盖)
ls -la > directory_output.txt
# 错误信息单独保存
cat nonexistent.txt 2> error.log
# 从文件读取数据并统计行数
wc -l < data.txt

文件描述符与重定向的关系

Linux系统为每个进程预定义了3个文件描述符(File Descriptor):

  • 0:标准输入 (stdin)
  • 1:标准输出 (stdout)
  • 2:标准错误 (stderr)

重定向本质上是将某个文件描述符指向其他文件或设备。

# 显式指定文件描述符(默认数字1可省略)
command 1> output.txt   # 等同于 command > output.txt
command 2> error.txt    # 错误信息重定向

标准错误与标准输出的分离与合并

1 分离输出

# 正常结果存入output,错误存入error
find / -name "*.conf" > found.conf 2> search_errors.log

2 合并输出(常见场景)

# 方法一:将stderr重定向到stdout(顺序重要)
command > output.txt 2>&1
# 方法二:使用Bash 4+的新语法
command &> output.txt
# 方法三:追加合并
command >> output.txt 2>&1

注意: 2>&1 必须放在 > 之后,否则相当于将stderr重定向到之前状态的stdout(通常是终端)。

3 丢弃不想要的输出

# 丢弃stdout
command > /dev/null
# 丢弃stderr
command 2> /dev/null
# 丢弃所有输出
command &> /dev/null

Here Document与Here String

1 Here Document(<<)

用于在脚本内嵌入多行输入:

#!/bin/bash
cat << EOF > config.ini
[server]
host = 127.0.0.1
port = 8080
EOF

2 Here String(<<<)

将字符串作为stdin传递给命令:

# 将字符串传递给grep
grep "error" <<< "line1 error\nline2 warning\nline3 error"

exec命令的高级重定向技巧

exec 可以在当前Shell中永久改变文件描述符的指向:

#!/bin/bash
# 将脚本内所有后续命令的stdout都写入文件
exec > script_output.log 2>&1
echo "This goes to log"
ls /nonexistent  # 错误也进入日志
echo "Another line"

应用场景: 将所有日志统一记录,避免每个命令手动重定向。

# 打开一个文件作为文件描述符3
exec 3> custom_fd.log
echo "Custom FD output" >&3
exec 3>&-  # 关闭文件描述符

使用tee命令同时输出到屏幕和文件

tee 命令可以分叉输出,同时显示在终端并写入文件:

# 显示进度同时记录日志
./long_running_task.sh | tee task_output.log
# 追加模式
./script.sh | tee -a session.log
# 同时捕获stderr(需通过重定向合并)
./script.sh 2>&1 | tee combined.log

实战案例:日志收集与错误处理脚本

#!/bin/bash
# 日志收集脚本 - log_collector.sh
# 设置日志文件
LOG_FILE="/var/log/app_$(date +%Y%m%d).log"
ERROR_FILE="/var/log/app_error_$(date +%Y%m%d).log"
# 将整个脚本的输出重定向到日志
exec > >(tee -a "$LOG_FILE") 2> >(tee -a "$ERROR_FILE" >&2)
echo "[INFO] Script started at $(date)"
# 模拟任务
for i in {1..5}; do
    echo "[DEBUG] Processing item $i"
    if [ $i -eq 3 ]; then
        echo "[ERROR] Item $i failed!" >&2
    fi
    sleep 1
done
echo "[INFO] Script completed"

执行效果:

  • 正常信息显示在终端并写入 app_日期.log
  • 错误信息显示在终端并写入 app_error_日期.log

常见问题与QA问答

❓ Q1: 2>&1&> 有何区别?

A:

  • 2>&1 是POSIX标准语法,在任何Shell中都可用(如sh、bash、zsh等),顺序很重要:必须先重定向stdout,再将stderr指向stdout。
  • &> 是Bash的简写语法,更直观但非标准,建议在新脚本中使用 2>&1 以保持兼容性。

❓ Q2: 如何将输出同时重定向到多个文件?

A: 使用 tee 命令或进程替换:

# 方法1: tee
command | tee file1 file2
# 方法2: 进程替换(Bash支持)
command > >(tee file1) >(tee file2)  # 不合理写法
# 正确做法
command | tee file1 | tee file2

❓ Q3: 重定向时文件内容被清空了怎么办?

A: 使用 >> 追加模式而非 > 覆盖模式。

# 错误:覆盖文件
echo "new" > important.txt  
# 正确:追加内容
echo "new" >> important.txt

❓ Q4: 如何关闭标准输出或标准错误?

A: 重定向到 /dev/null

# 关闭stdout
command > /dev/null
# 关闭stderr
command 2> /dev/null
# 关闭所有输出
command &> /dev/null

❓ Q5: 重定向对管道命令的影响是什么?

A: 管道 只处理stdout,不处理stderr,要同时传递stderr,需要先合并:

# 只处理stdout
command1 | command2
# 处理stdout和stderr
command1 2>&1 | command2

总结与最佳实践

  1. 日常日志:使用 >> 追加写入,避免丢失历史记录
  2. 错误处理:始终将stderr单独保存或显式合并
  3. 调试阶段:使用 tee 同时显示输出,不遗漏信息
  4. 脚本设计:在脚本开头使用 exec 统一重定向,简化后续代码
  5. 安全性:避免将敏感数据(如密码)通过重定向写入可公开访问的文件

掌握输入输出重定向是编写健壮Shell脚本的关键一步,它将简单命令串联为强大的自动化工具,让你的脚本能够优雅地处理数据流、记录运行状态、隔离错误信息,从今天起,在你的每个脚本中尝试运用这些技巧,你会发现自己解决问题的能力提升了一个台阶。

抱歉,评论功能暂时关闭!