Shell输入输出重定向

Shell 输入/输出重定向详解(新手详细版)

本文档面向零基础新手,从“标准输入、标准输出、标准错误是什么”讲起,详细说明重定向符号、覆盖与追加、合并错误与输出、管道、here 文档、常见用法等,并配有大量示例。


一、先弄清:程序的三条“标准流”

1.1 程序默认从哪里读、往哪里写?

在 Shell 里,一条命令(程序)默认有三个通道和外界打交道:

名称 全称 文件描述符编号 默认来源/去向 常见用途
标准输入 stdin 0 键盘 读用户输入、读管道左侧输出
标准输出 stdout 1 终端屏幕 正常结果、echo 输出
标准错误 stderr 2 终端屏幕 错误信息、警告
  • 标准输入(0):程序“读”的数据,默认是键盘;也可以被重定向成文件或管道。
  • 标准输出(1):程序“正常打印”的内容,默认显示在终端;可以重定向到文件或管道。
  • 标准错误(2):程序“报错、警告”的内容,默认也显示在终端;可以单独或和标准输出一起重定向。

为什么要分开 1 和 2?
这样可以把“正常结果”和“错误信息”分开处理,例如:正常结果写进文件,错误信息仍显示在屏幕上,或都写进同一个日志文件。

1.2 “重定向”在做什么?

重定向就是改变这些“通道”的来源或去向

  • 把本应输出到屏幕的内容,改写到文件
  • 把本应从键盘读的输入,改成从文件一段文字读;
  • 错误单独写到另一个文件,或和正常输出合并到一起。

下面按“输出重定向”“输入重定向”“管道”“here 文档”等分别说明。


二、输出重定向:把输出写到文件

2.1 覆盖写入:>

命令 > 文件 表示:把命令的标准输出(1)重定向到文件;若文件已存在则先清空再写(覆盖)。

echo "第一行" > out.txt
cat out.txt
# 第一行

echo "第二行" > out.txt
cat out.txt
# 第二行(原来的“第一行”被覆盖了)

注意> 只重定向标准输出重定向标准错误;错误信息仍会出现在屏幕上。

ls /存在 不存在的目录 > list.txt
# 终端上仍会看到“不存在的目录”的报错;list.txt 里只有“存在的目录”那一行的结果

2.2 追加写入:>>

命令 >> 文件 表示:把命令的标准输出追加到文件末尾,不删掉原有内容。

echo "第一行" >> log.txt
echo "第二行" >> log.txt
cat log.txt
# 第一行
# 第二行

常用于日志:每次运行都把新内容接到文件后面。

2.3 只重定向标准错误:2>、2>>

  • 命令 2> 文件:只把标准错误(2)写到文件,标准输出仍到屏幕;文件会被覆盖
  • 命令 2>> 文件:只把标准错误追加到文件。
# 把错误信息写进 err.txt,正常输出仍在屏幕
ls /不存在的路径 2> err.txt

# 追加错误到 err.log
some_cmd 2>> err.log

为什么是 2>?
“2”表示文件描述符 2(标准错误);“>”表示重定向到文件。合起来就是“把 2 重定向到文件”。

2.4 标准输出和标准错误都重定向

写法一:分别重定向到不同文件

# 正常输出到 out.txt,错误到 err.txt
ls /tmp /不存在的 1> out.txt 2> err.txt

写法二:都写到同一个文件(常见)

要把“正常输出”和“错误”都写进同一个文件,需要先把 2 重定向到 1 的当前去向,再重定向 1 到文件。顺序很重要:

# 正确:先定好 1 的去向(文件),再把 2 指向 1 的去向
ls /tmp /不存在的 > all.txt 2>&1

# 错误示例:2>&1 > file
# 这时 2 指向“当时的 1”(还是屏幕),然后 1 才改到 file,错误仍会到屏幕

简写(Bash):

# 都覆盖写入 file
ls /tmp /不存在的 &> all.txt
# 或
ls /tmp /不存在的 >& all.txt

# 都追加到 file(Bash)
ls /tmp /不存在的 >> all.txt 2>&1

小结

  • > file 2>&1:标准输出和标准错误都覆盖写到 file。
  • >> file 2>&1:都追加到 file。
  • &> file(Bash):都覆盖写到 file。

2.5 丢弃输出:/dev/null

/dev/null 是“黑洞”设备:写进去的内容会被丢弃,读出来是“空”。
常用于“不想要任何输出”时:

# 不关心正常输出,也不关心错误,都丢掉
some_cmd > /dev/null 2>&1
# 或
some_cmd &> /dev/null

# 只丢掉错误,正常输出仍显示
some_cmd 2> /dev/null

三、输入重定向:从文件或字符串读入

3.1 从文件读入:<

命令 < 文件 表示:把命令的标准输入(0)改成从文件读,而不是从键盘。

# 把 file.txt 的内容当作 wc 的输入
wc -l < file.txt
# 输出 file.txt 的行数

# sort 从 data.txt 读入并排序,结果打印到屏幕
sort < data.txt

和“把文件当参数”的区别

  • 命令 < 文件:文件内容作为输入流,程序通过“读标准输入”得到。
  • 命令 文件:文件名作为参数,程序自己打开文件读(如 cat file.txt)。

3.2 Here 文档:<< 标记

<< 标记 表示:从脚本里的“下一行”开始,直到遇到单独一行的“标记”为止,这一段文字都当作标准输入
适合在脚本里嵌入多行输入,不必先写进文件。

基本语法:

命令 << 结束标记
第一行内容
第二行内容
...
结束标记

示例:

cat << EOF
第一行
第二行
第三行
EOF
# 输出:
# 第一行
# 第二行
# 第三行

“结束标记”可以是任意字符串(如 EOF、END、STOP),只要单独成行且前后无空格即可。
若结束标记加引号(如 << 'EOF'),则中间内容不会做变量替换、命令替换;不加引号则会展开 $变量$(命令)

name="张三"
cat << EOF
你好,$name
EOF
# 你好,张三

cat << 'EOF'
你好,$name
EOF
# 你好,$name(字面)

3.3 Here 文档缩进:<<-

<<- 标记 表示:前导的 Tab 会被忽略,这样可以在脚本里把 here 文档缩进写,结束标记前也可以有 Tab(不能是空格)。

cat <<- EOF
    第一行
    第二行
    EOF

3.4 Here 字符串:<<<(Bash)

<<< 字符串 表示:把这一小段字符串直接当作命令的标准输入(单行时比 here 文档简洁)。

wc -l <<< "a
b
c"
# 3

grep "hello" <<< "hello world"
# hello world

四、文件描述符与重定向(了解即可)

  • n>:把文件描述符 n 重定向到文件(覆盖)。
  • n>>:追加。
  • n<:从文件读入到文件描述符 n。
  • &n:表示“当前文件描述符 n 的去向”;2>&1 即“把 2 指向 1 的当前去向”。
  • &-:关闭该描述符。

示例:

# 1 和 2 都到 file(前面已讲过)
cmd > file 2>&1

# 从 file 读入到标准输入(等价于 < file)
cmd 0< file

日常脚本里掌握 >>>2>2>&1<<<<<< 即可。


五、管道:|

5.1 管道做什么?

命令1 | 命令2 表示:把命令1 的标准输出接到命令2 的标准输入;命令1 的标准错误默认进管道,仍显示在终端。

ls -l | grep ".txt"
# ls 的输出作为 grep 的输入,grep 再筛选含 .txt 的行

cat file.txt | sort
# file.txt 的内容经 cat 输出,再作为 sort 的输入

5.2 让错误也进管道:2>&1 | …

若希望标准错误也参与管道,需要先把 2 合并到 1,再管道:

some_cmd 2>&1 | grep "error"

5.3 管道和重定向一起用

# 结果写文件,错误丢掉
ls -l | grep txt > result.txt 2>/dev/null

# 前一个命令输出写文件,同时再管道给下一个命令用 tee
ls -l | tee list.txt | wc -l

六、tee:一边写文件一边继续管道

tee 文件 会从标准输入读入,同时:① 写入指定文件,② 再原样输出到标准输出(可继续管道)。

# 输出既保存到 out.txt,又显示在屏幕
ls -l | tee out.txt

# 保存到文件,并继续管道给 wc
ls -l | tee list.txt | wc -l

tee -a 表示追加到文件,不覆盖。

echo "新行" | tee -a log.txt

七、进程替换(Bash):(…)

  • <(<(命令)):把命令的输出当作一个“文件名”传给需要文件的命令(实际是临时管道/设备)。
  • >(命令):把当前命令的输入接到另一个命令的输入(少用)。

示例:

# diff 需要两个“文件”,用两个命令的输出当“文件”
diff <(ls dir1) <(ls dir2)

# 把内容写入“某个命令的输入”
echo "hello" > >(cat)

了解即可,初学先掌握普通重定向和管道。


八、重定向顺序的影响

重定向是从左到右处理的;2>&1 表示“把 2 指向 1 的当前去向”。所以:

  • > file 2>&1:先把 1 改到 file,再把 2 指向 1(即 file)→ 都对。
  • 2>&1 > file:先把 2 指向 1(当时还是屏幕),再把 1 改到 file → 错误仍在屏幕,只有正常输出到 file。

建议:要“都进同一文件”时,写 > file 2>&1>> file 2>&1


九、实用示例汇总

9.1 只保存正常输出

ls -l > list.txt

9.2 只保存错误

my_script 2> error.log

9.3 正常和错误都进同一文件(覆盖/追加)

my_script > all.log 2>&1
my_script >> all.log 2>&1

9.4 完全静默(不想要任何输出)

my_script > /dev/null 2>&1
# 或
my_script &> /dev/null

9.5 从文件读入

sort < data.txt
while read -r line; do echo "$line"; done < file.txt

9.6 在脚本里嵌入多行输入(here 文档)

mysql -u user -p << EOF
SELECT * FROM t LIMIT 1;
EOF

9.7 单行字符串当输入(here 字符串)

grep "key" <<< "key=value"

9.8 既保存又显示(tee)

./install.sh | tee install.log

9.9 管道 + 过滤

ps aux | grep nginx
cat access.log | grep " 200 " | wc -l

十、常见坑与建议

  1. > 会覆盖文件:想保留原内容用 >>
  2. 2>&1 的顺序:要“都进同一文件”时,先重定向 1 再写 2>&1
  3. 管道只传标准输出:错误要进管道需先 2>&1|
  4. here 文档的结束标记要单独一行,且前后不要有多余空格。
  5. 变量/命令替换:here 文档不用引号会展开,用 ‘EOF’ 则不展开。

十一、符号速查表

写法 含义
> file 标准输出覆盖写到 file
>> file 标准输出追加到 file
2> file 标准错误覆盖写到 file
2>> file 标准错误追加到 file
> file 2>&1 标准输出和错误都覆盖写到 file
>> file 2>&1 都追加到 file
&> file 都覆盖写到 file(Bash)
< file 从 file 读入标准输入
<< 标记 here 文档(到“标记”行为止)
<<< 字符串 here 字符串(Bash)
| 管道:前命令标准输出 → 后命令标准输入
tee file 从标准输入读,写 file 并继续输出
/dev/null 丢弃输出或读入空

十二、小结

  • 三条流:标准输入(0)、标准输出(1)、标准错误(2);默认分别是键盘和屏幕。
  • 输出重定向> 覆盖、>> 追加;2> 只重定向错误;> file 2>&1&> file 都进同一文件;/dev/null 丢弃。
  • 输入重定向< 文件<< 标记 here 文档;<<< 字符串 here 字符串。
  • 管道| 连接标准输出到下一命令标准输入;要让错误也进管道先 2>&1
  • tee:同时写文件和继续管道;顺序注意 2>&1> file 之后。

多练:把命令结果写文件、追加日志、丢错误、从文件读、here 文档、管道加 grep,再结合 Shell变量.md 里的命令替换 $( ),输入输出重定向就能用熟。


文档以 Bash 为准;部分写法在 sh 或其它 Shell 中可能略有差异(如 &>、<<<)。

发表评论