管道与重定向 #

标准流 #

三种标准流 #

text
┌─────────────────────────────────────────────────────────────┐
│                    标准流                                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   stdin (0)    标准输入                                     │
│   stdout (1)   标准输出                                     │
│   stderr (2)   标准错误                                     │
│                                                             │
│   默认:                                                     │
│   stdin  -> 键盘                                            │
│   stdout -> 终端                                            │
│   stderr -> 终端                                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

输出重定向 #

基本重定向 #

bash
# 重定向输出到文件(覆盖)
$ echo "Hello" > file.txt

# 重定向输出到文件(追加)
$ echo "World" >> file.txt

# 重定向标准输出
$ ls -la > output.txt
$ ls -la 1> output.txt

# 重定向标准错误
$ ls /nonexistent 2> error.txt

# 重定向标准输出和标准错误
$ ls -la > all.txt 2>&1
$ ls -la &> all.txt
$ ls -la >& all.txt

# 追加标准输出和标准错误
$ ls -la >> all.txt 2>&1

# 丢弃输出
$ ls -la > /dev/null
$ ls -la 2> /dev/null
$ ls -la &> /dev/null

重定向到不同文件 #

bash
# 标准输出和标准错误分别重定向
$ command > output.txt 2> error.txt

# 标准输出追加,标准错误覆盖
$ command >> output.txt 2> error.txt

# 标准输出丢弃,保存错误
$ command > /dev/null 2> error.txt

输入重定向 #

基本输入重定向 #

bash
# 从文件读取输入
$ wc -l < file.txt

# 从文件读取并处理
$ sort < unsorted.txt > sorted.txt

# 从文件读取多行
$ cat << EOF
line 1
line 2
line 3
EOF

Here Document #

bash
# Here Document
$ cat << EOF
This is line 1
This is line 2
This is line 3
EOF

# 禁止变量展开
$ cat << 'EOF'
$HOME is not expanded
EOF

# 去除前导制表符
$ cat <<- EOF
		This line has leading tabs removed
	EOF

# 使用变量
$ name="World"
$ cat << EOF
Hello, $name!
EOF

# 在脚本中使用
#!/bin/bash
cat << EOF > config.ini
[database]
host = localhost
port = 3306
EOF

Here String #

bash
# Here String(单行)
$ cat <<< "Hello World"

# 使用变量
$ name="John"
$ cat <<< "Hello, $name"

# 传递给命令
$ bc <<< "2 + 2"
4

$ awk '{print $1}' <<< "Hello World"
Hello

管道 #

基本管道 #

bash
# 管道将一个命令的输出作为另一个命令的输入
$ ls -la | grep ".txt"
$ cat file.txt | sort | uniq

# 多级管道
$ cat access.log | grep "ERROR" | awk '{print $1}' | sort | uniq -c

# 管道与重定向组合
$ ls -la | grep ".txt" > txt_files.txt

管道与 tee #

bash
# tee 同时输出到文件和屏幕
$ ls -la | tee output.txt

# 追加模式
$ ls -la | tee -a output.txt

# 多个文件
$ ls -la | tee file1.txt file2.txt

# 管道中间使用 tee
$ cat file.txt | tee copy.txt | grep "pattern"

# 同时保存标准输出和标准错误
$ command 2>&1 | tee output.txt

管道与错误处理 #

bash
# 管道中的错误处理
# 默认管道返回最后一个命令的状态

# 使用 PIPESTATUS
$ false | true
$ echo ${PIPESTATUS[@]}
1 0

# 检查管道中每个命令的状态
$ command1 | command2 | command3
if [ ${PIPESTATUS[0]} -ne 0 ]; then
    echo "command1 failed"
fi

# 使用 set -o pipefail
$ set -o pipefail
$ false | true
$ echo $?
1

进程替换 #

进程替换语法 #

bash
# 进程替换
# <(command)   输出作为文件
# >(command)   输入作为文件

# 比较两个命令的输出
$ diff <(ls dir1) <(ls dir2)

# 合并输出
$ cat <(echo "first") <(echo "second")

# 发送输出到多个进程
$ echo "Hello" | tee >(grep -o "H" > h.txt) >(grep -o "e" > e.txt)

# 使用进程替换作为输入
$ sort <(cat file1.txt) <(cat file2.txt)

实用示例 #

bash
# 比较两个目录
$ diff -r <(ls -R dir1) <(ls -R dir2)

# 合并排序两个文件
$ sort -m <(sort file1.txt) <(sort file2.txt)

# 查找两个文件的差异
$ comm <(sort file1.txt) <(sort file2.txt)

# 使用多个输入
$ paste <(cut -f1 file.txt) <(cut -f3 file.txt)

# 日志分析
$ grep "ERROR" <(cat log1.txt) <(cat log2.txt)

文件描述符 #

自定义文件描述符 #

bash
# 打开文件描述符
$ exec 3> output.txt    # 打开文件描述符 3 用于写入

# 写入文件描述符
$ echo "Hello" >&3

# 读取文件描述符
$ exec 4< input.txt     # 打开文件描述符 4 用于读取
$ read line <&4

# 关闭文件描述符
$ exec 3>&-             # 关闭写入
$ exec 4<&-             # 关闭读取

# 读写模式
$ exec 3<> file.txt     # 打开文件描述符 3 用于读写

# 复制文件描述符
$ exec 4>&1             # 将 stdout 复制到 4
$ exec 1> output.txt    # 重定向 stdout
$ echo "To file"
$ exec 1>&4             # 恢复 stdout
$ echo "To terminal"

实用示例 #

bash
#!/bin/bash

# 同时写入多个文件
exec 3> output1.txt
exec 4> output2.txt

echo "Line 1" | tee >&3 >&4
echo "Line 2" | tee >&3 >&4

exec 3>&-
exec 4>&-

# 日志记录
exec 3> >(tee -a log.txt)
echo "Log message" >&3
exec 3>&-

重定向技巧 #

重定向顺序 #

bash
# 重定向顺序很重要

# 正确:先重定向 stdout,再重定向 stderr
$ command > file.txt 2>&1

# 错误:stderr 仍然输出到终端
$ command 2>&1 > file.txt

# 解释:
# 2>&1 将 stderr 重定向到 stdout 当前指向的位置
# 在第一个例子中,stdout 已经重定向到 file.txt
# 在第二个例子中,stdout 还指向终端

常见模式 #

bash
# 静默模式(丢弃所有输出)
$ command &> /dev/null

# 只保存错误
$ command > /dev/null 2> error.txt

# 保存所有输出
$ command &> output.txt

# 分别保存
$ command > output.txt 2> error.txt

# 同时显示和保存
$ command 2>&1 | tee output.txt

# 追加模式
$ command >> output.txt 2>> error.txt

调试重定向 #

bash
# 查看文件描述符
$ ls -la /proc/$$/fd/

# 查看打开的文件
$ lsof -p $$

# 查看重定向状态
$ exec

小结 #

通过本节学习,你应该掌握:

  1. 标准流:stdin、stdout、stderr
  2. 输出重定向:>、>>、2>、&>
  3. 输入重定向:<、<<、<<<
  4. 管道:|、tee
  5. 进程替换:<()、>()
  6. 文件描述符:exec、自定义描述符

下一步,我们将学习高级工具。

最后更新:2026-04-11