Shell重定向之`>/dev/null 2>&1`

在看 shell 脚本时,经常会看到这样一段写法:

command >/dev/null 2>&1

很多人第一次看到会觉得有点绕,尤其是 2>&1 这部分,不太容易一眼看明白。

这篇文章就专门整理一下它的含义,以及为什么顺序不能乱写。

一、这段写法到底是什么意思

command >/dev/null 2>&1

它表示:

  • 把标准输出重定向到 /dev/null
  • 再把标准错误重定向到标准输出当前所在的位置

最终效果就是:

正常输出和报错输出都会被丢弃,不在终端显示。

二、先理解 3 个标准通道

在 Unix/Linux 里,一个进程默认会带有 3 个标准通道,对应 3 个文件描述符:

0 = stdin
1 = stdout
2 = stderr

分别表示:

  • 0:标准输入,程序从哪里读数据
  • 1:标准输出,程序正常结果输出到哪里
  • 2:标准错误,程序报错信息输出到哪里

后面提到的 1>2>2>&1,本质上都是在操作这些文件描述符。

三、>/dev/null 是什么意思

command >/dev/null

它等价于:

command 1>/dev/null

意思是把标准输出重定向到 /dev/null

这里有两个点:

  • > 表示输出重定向
  • /dev/null 是一个特殊设备文件,写进去的内容会被直接丢弃

所以这条命令的效果是:

  • 正常输出不再显示
  • 错误输出仍然会显示在终端

例如:

ls existing-file missing-file >/dev/null

这时 existing-file 的正常输出会被丢弃,但如果 missing-file 不存在,报错信息还是会显示出来。

四、2>&1 是什么意思

2>&1

它的含义是:

把标准错误重定向到文件描述符 1 当前所指向的位置。

拆开看会更清楚:

  • 2>:操作标准错误
  • &1:表示引用文件描述符 1

注意这里的 1 不是文件名,而是文件描述符编号。

也就是说:

command >/dev/null 2>&1

可以理解成:

  1. 先把标准输出丢到 /dev/null
  2. 再让标准错误也跟着标准输出走

所以最后标准输出和标准错误都会进入 /dev/null

五、为什么 1 前面要加 &

因为 shell 需要区分:

  • 你写的是文件描述符
  • 还是普通文件名

不加 &

command 2>1

这表示把标准错误写入一个名为 1 的文件。

加上 &

command 2>&1

这表示把标准错误重定向到文件描述符 1

所以 & 的作用很简单,就是告诉 shell:

后面的数字不是文件名,而是文件描述符。

六、为什么 1 可以省略

在 shell 里:

>file

默认就是:

1>file

因为如果 > 前面没有写编号,默认操作的就是标准输出。

所以:

>/dev/null

等价于:

1>/dev/null

七、顺序为什么很重要

很多人会把下面两种写法看成一样,其实它们不一样。

写法 A

command >/dev/null 2>&1

执行顺序是:

  1. 1 指向 /dev/null
  2. 2 指向 1 当前的位置,也就是 /dev/null

结果:

  • 标准输出被丢弃
  • 标准错误也被丢弃

写法 B

command 2>&1 >/dev/null

执行顺序是:

  1. 先让 2 指向 1 当前的位置,此时 1 还是终端
  2. 再把 1 重定向到 /dev/null

结果:

  • 标准输出被丢弃
  • 标准错误仍然输出到终端

所以这两条命令不是一回事:

command >/dev/null 2>&1
command 2>&1 >/dev/null

重点就在于 shell 会从左到右依次处理重定向。

八、常见用法

1. 只隐藏正常输出

command >/dev/null

2. 只隐藏错误输出

command 2>/dev/null

3. 同时隐藏正常输出和错误输出

command >/dev/null 2>&1

4. 正常输出和错误输出都写入同一个文件

command >all.log 2>&1

5. 正常输出写文件,错误仍然显示在终端

command >out.log

6. 错误写文件,正常输出仍然显示在终端

command 2>err.log

九、/dev/null 是什么

/dev/null 是 Unix/Linux 下的一个特殊设备文件,常被叫做“黑洞设备”。

它的特点很简单:

  • 写进去的内容会被丢弃
  • 从里面通常读不到有意义的内容

所以它特别适合拿来做静默执行,比如:

  • 不想看到命令的普通输出
  • 不想让脚本打印一堆无关信息
  • 想让某些命令安静执行

十、实际例子

比如在脚本里,你可能会看到:

source venv/bin/activate >/dev/null 2>&1

它表示:

  • 执行 venv/bin/activate
  • 不显示正常输出
  • 不显示错误输出

不过这里也要注意一件事:

如果激活失败,报错信息也会被隐藏掉。

所以在排查问题时,通常要先把这类重定向去掉,不然看不到真实报错。

十一、还有一种更简写的写法

bashzsh 里,经常还能看到:

command &>/dev/null

它通常等价于:

command >/dev/null 2>&1

不过如果考虑可读性和兼容性,很多时候还是传统写法更直观一些:

command >/dev/null 2>&1

十二、小结

>/dev/null 2>&1 本质上就是 shell 重定向语法。

它的含义可以概括成一句话:

先把标准输出重定向到 /dev/null,再把标准错误重定向到标准输出当前所在的位置,从而让正常输出和错误输出都被丢弃。

如果只想记住最关键的几条,可以直接记下面这个速记版:

0 = stdin
1 = stdout
2 = stderr
>/dev/null      # 等价于 1>/dev/null,隐藏标准输出
2>/dev/null # 隐藏标准错误
2>&1 # 让标准错误跟随标准输出
>/dev/null 2>&1 # 正常输出和错误输出都隐藏
2>1   # 写入名为 1 的文件
2>&1 # 重定向到文件描述符 1

参考文章