Bash 进程替换

作者:Mitch Frazier

除了常见的输入/输出重定向形式之外,shell 还识别一种叫做 *进程替换* 的东西。虽然没有作为输入/输出重定向的一种形式进行文档化,但它的语法和效果是相似的。

进程替换的语法是

  <(list)
or
  >(list)
其中每个 *list* 是一个命令或命令管道。进程替换的效果是使每个 list 就像一个文件一样。这是通过在文件系统中给 list 一个 *名称*,然后在命令行中替换该名称来完成的。list 通过连接到命名管道或使用以下文件来获得名称:/dev/fd(如果操作系统支持)。通过这样做,命令只会看到一个文件名,而不知道它正在从命令管道读取或写入。

要将命令管道替换为输入文件,语法是

  command ... <(list) ...
要将命令管道替换为输出文件,语法是
  command ... >(list) ...

起初,进程替换可能看起来相当没有意义,例如,你可能会想到一些简单的事情,比如

  uniq <(sort a)
对文件进行排序,然后找到其中的唯一行,但更常见(也更方便)的写法是
  sort a | uniq
当你有多个要连接到单个命令的命令管道时,进程替换的威力就体现出来了。

例如,给定两个文件

  # cat a
  e
  d
  c
  b
  a
  # cat b
  g
  f
  e
  d
  c
  b
要查看这两个未排序文件中每个文件独有的行,你可以这样做
  # sort a | uniq >tmp1
  # sort b | uniq >tmp2
  # comm -3 tmp1 tmp2
  a
        f
        g
  # rm tmp1 tmp2
通过进程替换,我们可以用一行代码完成所有这些
  # comm -3 <(sort a | uniq) <(sort b | uniq)
  a
        f
        g

根据您的 shell 设置,您可能会收到类似于以下内容的错误消息

  syntax error near unexpected token `('
当您尝试使用进程替换时,尤其是在 shell 脚本中使用时。 进程替换不是 POSIX 兼容功能,因此可能需要通过以下方式启用:
  set +o posix
小心不要尝试类似的事情
  if [[ $use_process_substitution -eq 1 ]]; then
    set +o posix
    comm -3 <(sort a | uniq) <(sort b | uniq)
  fi
命令set +o posix不仅启用了进程替换的执行,还启用了语法的识别。 因此,在上面的示例中,shell 会在 "set" 命令执行之前尝试解析进程替换语法,因此仍然将进程替换语法视为非法。

当然,请注意,并非所有 shell 都支持进程替换,这些示例将在 bash 中有效。

Mitch Frazier 是 Emerson Electric Co. 的一名嵌入式系统程序员。自 2000 年代初以来,Mitch 一直是 *Linux Journal* 的贡献者和朋友。

加载 Disqus 评论