Shell 脚本中的重定向和管道机制是进程间通信和数据流控制的重要方式。
标准输入输出
三种标准流
bashstdin (0) # 标准输入 - 默认从键盘读取 stdout (1) # 标准输出 - 默认输出到屏幕 stderr (2) # 标准错误 - 默认输出到屏幕
输出重定向
重定向标准输出
bash# 覆盖重定向(>) echo "Hello" > file.txt # 创建或覆盖文件 ls -l > filelist.txt # 追加重定向(>>) echo "World" >> file.txt # 追加到文件末尾 date >> log.txt
重定向标准错误
bash# 重定向错误输出 ls /nonexistent 2> error.log # 追加错误输出 ls /nonexistent 2>> error.log # 同时重定向输出和错误 command > output.txt 2>&1 command &> output.txt # Bash 4.0+ 简写 # 分别重定向 command > output.txt 2> error.txt
丢弃输出
bash# 丢弃标准输出 command > /dev/null # 丢弃错误输出 command 2> /dev/null # 丢弃所有输出 command > /dev/null 2>&1 command &> /dev/null
输入重定向
从文件读取
bash# 从文件读取输入 wc -l < file.txt # 多行输入 cat << EOF > script.sh #!/bin/bash echo "Hello, World!" EOF
Here Document
bash# 多行输入 cat << EOF This is line 1 This is line 2 This is line 3 EOF # 使用变量 name="John" cat << EOF Hello, $name! EOF # 禁用变量替换 cat << 'EOF' This is $name - not expanded EOF
Here String
bash# 单行输入 wc -w <<< "Hello World" # 处理变量 text="Hello World" grep "World" <<< "$text"
管道
基本管道
bash# 将一个命令的输出作为另一个命令的输入 ps aux | grep nginx cat file.txt | grep "pattern" ls -l | sort -k5 -n # 多个管道连接 cat file.txt | grep "pattern" | wc -l
管道与重定向结合
bash# 管道输出到文件 command | tee output.txt # 管道错误输出 command 2>&1 | grep "error" # 从管道读取输入 while read line; do echo "Line: $line" done < <(ls -l)
进程替换
基本语法
bash# 输出进程替换(<()) diff <(ls dir1) <(ls dir2) # 输入进程替换(>()) tar -cf >(gzip > archive.tar.gz) directory/ # 多个进程替换 paste <(cut -f1 file1) <(cut -f2 file2)
实际应用
bash# 比较两个目录 diff <(ls dir1) <(ls dir2) # 合并多个文件 paste <(cut -d: -f1 /etc/passwd) <(cut -d: -f3 /etc/passwd) # 实时监控 tail -f /var/log/syslog | grep --line-buffered "ERROR" | tee errors.log
高级重定向
文件描述符操作
bash# 打开自定义文件描述符 exec 3> output.txt echo "Line 1" >&3 echo "Line 2" >&3 exec 3>&- # 从文件描述符读取 exec 4< input.txt read line <&4 echo "$line" exec 4<&- # 交换文件描述符 command 3>&1 1>&2 2>&3
同时读写
bash# 同时读写同一文件 exec 3<> file.txt read -u 3 line echo "New content" >&3 exec 3>&-
实际应用示例
日志处理
bash# 分离正常输出和错误输出 ./script.sh 1> success.log 2> error.log # 合并日志 ./script.sh > all.log 2>&1 # 实时监控日志 tail -f /var/log/app.log | grep --line-buffered "ERROR" | tee errors.log
数据处理
bash# 处理 CSV 文件 cat data.csv | cut -d, -f1,3 | sort -u > output.txt # 统计信息 cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn > stats.txt # 批量处理 find . -name "*.txt" -exec cat {} \; | grep "pattern" > results.txt
系统管理
bash# 备份重要命令输出 df -h > disk_usage_$(date +%Y%m%d).txt ps aux > process_list_$(date +%Y%m%d).txt # 监控系统 while true; do date >> monitor.log free -m >> monitor.log echo "---" >> monitor.log sleep 60 done
脚本开发
bash# 捕获命令输出 result=$(command) echo "Result: $result" # 处理多行输出 while IFS= read -r line; do echo "Processing: $line" done < <(find . -name "*.sh") # 创建临时文件 tmpfile=$(mktemp) command > "$tmpfile" # 处理文件 rm -f "$tmpfile"
最佳实践
- 使用 /dev/null 丢弃不需要的输出: 减少日志噪音
- 分别处理 stdout 和 stderr: 便于调试和日志分析
- 使用管道组合命令: 提高处理效率
- 使用 tee 同时输出和保存: 便于实时监控
- 注意管道的缓冲: 使用
--line-buffered处理实时数据 - 使用进程替换: 避免创建临时文件
- 及时关闭文件描述符: 防止资源泄漏
- 使用 mktemp 创建临时文件: 避免文件名冲突