文章目录
- [1. 核心概念](#1. 核心概念)
-
- [1.1 文件描述符(File Descriptors)](#1.1 文件描述符(File Descriptors))
- [1.2 重定向概念图](#1.2 重定向概念图)
- [2. 输出重定向详解](#2. 输出重定向详解)
-
- [2.1 基础输出重定向](#2.1 基础输出重定向)
- [2.2 合并输出重定向](#2.2 合并输出重定向)
- [2.3 特殊输出目标](#2.3 特殊输出目标)
- [3. 输入重定向详解](#3. 输入重定向详解)
-
- [3.1 基础输入重定向](#3.1 基础输入重定向)
- [3.2 Here Document(文档内嵌)](#3.2 Here Document(文档内嵌))
- [3.3 Here String(字符串输入)](#3.3 Here String(字符串输入))
- [4. 文件描述符高级操作](#4. 文件描述符高级操作)
-
- [4.1 自定义文件描述符](#4.1 自定义文件描述符)
- [4.2 文件描述符复制与移动](#4.2 文件描述符复制与移动)
- [4.3 输入文件描述符](#4.3 输入文件描述符)
- [5. 管道符(|)的深入理解](#5. 管道符(|)的深入理解)
-
- [5.1 基础管道](#5.1 基础管道)
- [5.2 管道与文件描述符](#5.2 管道与文件描述符)
- [5.3 管道的工作原理](#5.3 管道的工作原理)
- [6. tee命令:分流输出](#6. tee命令:分流输出)
-
- [6.1 tee基础用法](#6.1 tee基础用法)
- [6.2 tee高级用法](#6.2 tee高级用法)
- [6.3 tee与自定义文件描述符](#6.3 tee与自定义文件描述符)
- [7. 进程替换](#7. 进程替换)
-
- [7.1 基本概述](#7.1 基本概述)
- [7.2 输入进程替换](#7.2 输入进程替换)
- [7.2 输出进程替换](#7.2 输出进程替换)
- [8. 最佳实践与陷阱](#8. 最佳实践与陷阱)
-
- [8.1 常见陷阱](#8.1 常见陷阱)
- [8.2 性能考虑](#8.2 性能考虑)
- [9. 快速参考表](#9. 快速参考表)
1. 核心概念
1.1 文件描述符(File Descriptors)
文件描述符是操作系统用来跟踪打开文件的整数标识符,Shell中有三个标准文件描述符:
| 文件描述符 | 名称 | 默认设备 | 缩写 |
|---|---|---|---|
| 0 | 标准输入 | 键盘 | stdin |
| 1 | 标准输出 | 屏幕 | stdout |
| 2 | 标准输出 | 屏幕 | stderr |
1.2 重定向概念图
标准输出
标准错误
标准输入
连接命令
命令执行
输出类型
文件/设备
文件/设备
输入源
管道
下一个命令
文件
屏幕
dev/null
文件
屏幕
dev/null
2. 输出重定向详解
2.1 基础输出重定向
| 语法 | 说明 | 示例 | 应用场景 |
|---|---|---|---|
cmd > file |
标准输出重定向到文件(覆盖) | ls > list.txt |
保存命令结果到文件 |
cmd >> file |
标准输出重定向到文件(追加) | date >> log.txt |
日志记录 |
cmd 2> file |
标准错误重定向到文件(覆盖) | grep "x" nofile 2> error.log |
错误日志分离 |
cmd 2>> file |
标准错误重定向到文件(追加) | cmd 2>> errors.txt |
错误信息收集 |
2.2 合并输出重定向
| 语法 | 说明 | 示例 | 应用场景 |
|---|---|---|---|
cmd > file 2>&1 |
标准输出和错误都重定向到文件 | script.sh > output.log 2>&1 |
完整日志记录 |
cmd &> file |
同上(Bash简写) | cmd &> all_output.txt |
Bash中简化写法 |
cmd > file1 2> file2 |
输出和错误分别到不同文件 | make > build.log 2> error.log |
分离正常和错误信息 |
示例详解:
bash
# 错误示例:顺序很重要!
command 2>&1 > file # 错误!stderr仍输出到屏幕
command > file 2>&1 # 正确!两者都到文件
# 解释:
# 2>&1 表示 "将stderr重定向到stdin当前指向的位置"
# 所以必须先重定向stdout,再重定向stderr
2.3 特殊输出目标
bash
# 丢弃输出(黑洞设备)
command > /dev/null # 丢弃标准输出
command 2> /dev/null # 丢弃标准错误
command &> /dev/null # 丢弃所有输出
# 实际应用:静默执行
if ping -c1 server &> /dev/null; then
echo "Server is up"
fi
3. 输入重定向详解
3.1 基础输入重定向
| 语法 | 说明 | 示例 | 应用场景 |
|---|---|---|---|
cmd < file |
从文件读取输入 | wc -l < data.txt |
统计文件行数 |
cmd << EOF |
Here Document | 见下方示例 | 脚本内嵌数据 |
cmd <<< "str" |
Here String | grep "x" <<< "text" |
字符串作为输入 |
3.2 Here Document(文档内嵌)
bash
# 基本用法
cat << EOF
这是多行文本
第二行内容
第三行内容
EOF
# 实际应用:自动化配置
ftp -n << END_FTP
open $SERVER
user $USER $PASS
binary
put $FILE
quit
END_FTP
# 抑制变量替换(单引号)
cat << 'EOF'
当前路径是:$PWD # 这里$PWD不会展开
EOF
3.3 Here String(字符串输入)
bash
# 传递字符串作为输入
tr 'a-z' 'A-Z' <<< "hello world"
# 输出:HELLO WORLD
# 实际应用:快速测试
md5sum <<< "test string"
4. 文件描述符高级操作
4.1 自定义文件描述符
bash
# 创建自定义文件描述符
exec 3> custom.log # 创建FD3用于输出
echo "Log message" >&3 # 写入FD3
exec 3>&- # 关闭FD3
# 实际应用:多日志文件
exec 3> app.log
exec 4> error.log
echo "Info message" >&3
echo "Error occurred" >&4
exec 3>&-
exec 4>&-
4.2 文件描述符复制与移动
bash
# 保存和恢复标准输出
exec 3>&1 # 保存stdout到FD3
exec 1> output.log # 重定向stdout到文件
echo "This goes to file" # 输出到文件
exec 1>&3 # 恢复stdout
echo "Back to terminal" # 输出到终端
# 实际应用:临时重定向
log_to_file() {
exec 3>&1 1>>"$LOG_FILE" # 保存并重定向
echo "[$(date)] $*"
exec 1>&3 3>&- # 恢复并关闭
}
4.3 输入文件描述符
bash
# 从多个输入源读取
exec 3< input1.txt
exec 4< input2.txt
read -u 3 line1 # 从FD3读取
read -u 4 line2 # 从FD4读取
exec 3<&-
exec 4<&-
5. 管道符(|)的深入理解
5.1 基础管道
|将command1的标准输出作为command2的标准输入
bash
# 基本管道
command1 | command2 # command1的标准输出作为command2的标准输入
# 实际示例
ps aux | grep ssh # 查找SSH进程
cat file.txt | sort | uniq # 排序并去重
5.2 管道与文件描述符
bash
# 管道只处理stdout,不处理stderr
ls nofile | wc -l # stderr显示在屏幕,管道传递空输出
# 重定向stderr到管道
ls nofile 2>&1 | wc -l # 错误信息也通过管道传递
5.3 管道的工作原理
关键特性
并行执行
匿名管道
单向通信
命令1
管道缓冲区
命令2
6. tee命令:分流输出
6.1 tee基础用法
tee命令从标准输入读取,并同时写入标准输出和文件。
bash
# 基本语法
command | tee file # 输出到屏幕和文件
command | tee -a file # 追加到文件
# 实际应用
make 2>&1 | tee build.log # 编译并保存日志
6.2 tee高级用法
bash
# 多个输出目标
ls -l | tee file1 file2 file3
# 与管道结合
ls -l | tee file.log | grep "txt" | wc -l
# 实际应用:调试管道
cat data.txt | tee raw_data.log | grep "error" | tee errors.log | wc -l
6.3 tee与自定义文件描述符
bash
# 保存中间结果
process_data() {
input_data | tee >(process1) >(process2) > /dev/null
}
# 实际应用:并行处理
cat bigfile.txt | \
tee >(grep "error" > errors.txt) \
>(grep "warning" > warnings.txt) \
>(wc -l > count.txt) \
> /dev/null
7. 进程替换
7.1 基本概述
我们执行的命令是需要参数的。有些命令的参数是文件,而进程替换的意思就是使用进程去替换文件的位置 。
输入进程替换 :所有子进程的执行结果都会被保存到对应的临时文件中。所有子进程执行完毕后,主进程再执行。主进程以所有输入进程的临时文件作为输入参数
输出进程替换:主进程的输出文件参数被输出进程替换。主进程的所有输出都会被重定向到一个临时文件中,所有子进程以这个临时文件作为自己的输入文件参数
7.2 输入进程替换
diff命令用来比较两个文件的差异。现在这两个文件被两个输入进程替换掉了。bash会先起两个进程,执行进程中的命令,命令的输出结果会被分别保存到两个临时文件中。进程执行完毕后,diff命令比较两个临时文件的差异。
bash
# 比较目录列表的差异
diff <(ls /path/to/dir1) <(ls /path/to/dir2)
# 比较文件排序后的差异
diff <(sort file1.txt) <(sort file2.txt)
paste命令用来合并多个文件。在这里,paste命令会等待三个输入进程执行完毕后,合并他们的临时文件
bash
# 同时读取多个命令的输出
paste <(cat file1) <(wc -l file2) <(date)
# 输出示例(三列):# file1第一行内容 file2行数 当前日期时间
7.2 输出进程替换
tee命令的输出文件,被输出进程替换掉了,tee命令的所有输出,都会作为三个子进程的输入。
bash
# 将输出同时发送到多个处理流程
echo "测试数据" | tee >(grep "测试" > test.txt) \
>(wc -l > count.txt) \
>(sha256sum > hash.txt)
# 查看结果
cat test.txt count.txt hash.txt
8. 最佳实践与陷阱
8.1 常见陷阱
bash
# 陷阱1:顺序错误
cmd 2>&1 > file # 错误!stderr不重定向到文件
cmd > file 2>&1 # 正确
# 陷阱2:管道中的stderr
cmd1 | cmd2 # stderr不通过管道
cmd1 2>&1 | cmd2 # stderr也通过管道
# 陷阱3:Here Document的空格
cat << EOF # 正确
cat << EOF # 错误(多空格)
8.2 性能考虑
bash
# 避免不必要的tee
# 不佳:多次写磁盘
cat file | tee temp1 | process1 | tee temp2 | process2
# 改进:需要时才保存中间结果
if [ "$DEBUG" = "true" ]; then
cat file | tee debug1.log | process1 | tee debug2.log | process2
else
cat file | process1 | process2
fi
9. 快速参考表
| 操作 | 语法 | 说明 |
|---|---|---|
| 标准输出到文件 | cmd > file |
覆盖写入 |
| 追加输出 | cmd >> file |
追加写入 |
| 标准错误到文件 | cmd 2> file |
错误输出 |
| 两者都重定向 | cmd &> file |
Bash简写 |
| 输入重定向 | cmd < file |
从文件读取 |
| Here Document | cmd << MARKER |
内嵌文档 |
| Here String | cmd <<< "text" |
字符串输入 |
| 管道 | `cmd1 | cmd2` |
| 分流输出 | `cmd | tee file` |
| 自定义FD输出 | cmd >&3 |
写入FD3 |
| 自定义FD输入 | cmd <&4 |
从FD4读取 |
| 关闭FD | 3>&- |
关闭FD3 |