在 shell 中使用重定向和管道

在编写 shell 脚本时,容易对重定向与管道的概念产生混淆。本文总结了一些关于在 shell 中运用重定向和管道的经验。

  • 重定向

    重定向,即输入输出重定向。文章主要研究对象为 linux 标准输入、标准输出和标准错误输出,linux 定义了 0、1、2 三个文件描述符,分别对应标准输入、标准输出和标准错误输出。
    • 输出重定向

    输出重定向符
    1> 标准输出重定向符,可简写为 >
    2> 标准错误重定向符
    &> 标准输出与标准错误输出重定向符(在某些场合使用会出问题,如 crontab。尽量用 cmd > file 2>&1 代替)
    1>&2 标准输出重定向至标准错误输出(作为整体理解)
    2>&1 标准错误输出重定向至标准输出(作为整体理解)
    1>> 标准输出重定向,追加
    2>> 标准错误输出重定向,追加
    重定向至文件
    <linux shell command> 输出重定向符 <linux file>,如:
    ls > abc
    ls 2> def
    文件描述符重定向
    ls > abc 2>&1
    tmp=`git status 2>&1`
    • 输入重定向

      < 标准输入重定向
      <linux shell command> 输入重定向符 <linux file>,把 linux 文件内容作为 shell 命令的输入,如:
      wc < /etc/fstab
      << 分隔符控制输入重定向
      <linux shell command> 分隔符控制输入重定向 分隔符
      输入内容1
      输入内容2
      输入内容3
      ...
      分隔符
      分隔符之间的内容作为 shell 命令的输入
      vi myfile <<abcdef
      i
      This is line 1
      This is line 2
      ^[
      zz
      abcdef
    • 小结

      除文件描述符重定向、分隔符控制输入重定向外,其他重定向符的一端是 shell 命令,另一端是 linux 文件。要么是 linux 文件内容作为 shell 命令的输入,要么是 linux 文件存放 shell 命令的输出结果。
      如果不使用重定向符,则 shell 命令的默认输入为标准输入:键盘; shell 命令的默认输出为标准输出:显示器。从 linux 操作系统角度,标准输入输出都是 linux 文件,把默认输入输出看成隐形的重定向,则隐形重定向符的一端仍是 shell 命令,另一端仍是 linux 文件。
  • 管道

    | 管道符
    <linux shell command> 管道符 <linux shell command>,管道符连接两个 shell 命令,把前一个 shell 命令的标准输出作为后一个 shell 命令的标准输入,如:
    ls | wc
    cat /etc/fstab | wc

小结:使用管道时,管道符两端都是 shell 命令

  • 区分重定向和管道

    简而言之,重定向shell 命令和 linux 文件之间起作用,管道在两个 shell 命令之间起作用
    此外,管道的实现机制是 linux 的 IPC 机制,即进程间通信,使用管道时,系统会使用新的进程运行管道中的 shell 命令。因此,当前 shell 中的变量与管道中 shell 命令使用的变量存在于不同的进程环境中,管道中 shell 命令改变的变量不会影响当前 shell 中的变量
  • 补充:用户自定义变量与标准输出

shell 命令的 标准输出 可给用户自定义变量赋值,但 标准错误输出 不能直接给用户自定义变量赋值

a=`ls`
echo "$a"

几个例子:
获取 git 的执行结果(1)

a=`git status -s`
echo $?
echo $a

如果第一行命令执行出错,标准错误输出 将在屏幕上显示错误信息,但 a 的值为空,因为 a 没有从 标准输出 获取到任何内容
获取 git 的执行结果(2)

a=`git status -s 2>&1`
echo $?
echo $a

第一行命令把 标准错误输出 重定向至 标准输出,此时 a 能够获取 git 执行结果的报错信息
获取 git 的执行结果(3)

a=`git status -s 2>/dev/null`
echo $?
echo $a

第一行命令把 标准错误输出 重定向至 /dev/null,屏幕上将不会出现任何报错信息,a 仍从 标准输出 获取 git 的执行结果