什么是实现流程替代的可移植(POSIX)方法?


25

一些shell(例如bash)支持Process Substitution,这是一种将过程输出显示为文件的方式,如下所示:

$ diff <(sort file1) <(sort file2)

但是,此构造不是POSIX,因此不可移植。如何处理替代的实现POSIX -友好的方式(即其中一个在工作/bin/sh

注意:问题不是问如何区分两个排序的文件-这只是一个演示流程替换的人为例子!




1
POSIX不需要/bin/sh是POSIX shell,只需要sh在适当的环境中符合POSIX要求的某个地方调用一个命令即可。例如,在Solaris 10上,/bin/sh它不是POSIX外壳,而是一个古老的Bourne外壳,而POSIX外壳在中/usr/xpg4/bin/sh
斯特凡Chazelas

Answers:


17

该功能由ksh(首次在ksh86中记录)引入,并利用了该/dev/fd/n功能(先前在某些BSD和AT&T系统中独立添加)。在kshksh93u及更高版本中,除非您的系统支持/ dev / fd / n,否则它将无法正常工作。zsh,bash及ksh93u+更高版本可以使用临时命名管道(我相信SysIII中添加了命名管道),其中/ dev / fd / n不可用。

在可用的系统上(POSIX未指定那些系统),您可以使用以下方法自己进行进程替换():/dev/fd/ndiff <(cmd1) <(cmd2)

{
  cmd1 4<&- | {
    # in here fd 3 points to the reading end of the pipe
    # from cmd1, while fd 0 has been restored from the original
    # stdin (saved on fd 4, now closed as no longer needed)

    cmd2 3<&- | diff /dev/fd/3 -

  } 3<&0 <&4 4<&- # restore the original stdin for cmd2

} 4<&0 # save a copy of stdin for cmd2

但是,这ksh93在Linux上不起作用,shell管道是用套接字对而不是管道实现的,并且/dev/fd/3fd 3指向套接字的打开在Linux上不起作用。

尽管POSIX没有指定。它确实指定了命名管道。命名管道的工作方式与普通管道类似,只是您可以从文件系统访问它们。这里的问题是,您必须创建临时文件并随后进行清理,这很难可靠地完成,特别是考虑到POSIX没有标准机制(如某些系统上的标准)来创建临时文件或目录,并且可移植地进行信号处理(挂断或杀死时进行清理)也很难移植。/dev/fd/nmktemp -d

您可以执行以下操作:

tmpfifo() (
  n=0
  until
    fifo=$1.$$.$n
    mkfifo -m 600 -- "$fifo" 2> /dev/null
  do
    n=$((n + 1))
    # give up after 20 attempts as it could be a permanent condition
    # that prevents us from creating fifos. You'd need to raise that
    # limit if you intend to create (and use at the same time)
    # more than 20 fifos in your script
    [ "$n" -lt 20 ] || exit 1
  done
  printf '%s\n' "$fifo"
)

cleanup() { rm -f -- "$fifo"; }
fifo=$(tmpfifo /tmp/fifo) || exit

cmd2 > "$fifo" & cmd1 | diff - "$fifo"
rm -f -- "$fifo"

(此处不处理信号处理)。


命名管道示例对我来说是完全清楚的(我想10是一个任意限制吗?),但我无法弄清您的/dev/fd/n示例。为什么描述符4关闭两次?(描述符3也是如此。)我迷路了。
通配符

@Wildcard,对于不需要它的命令已关闭。在这里,仅diff需要fd3。不需要fd 4,它仅用于将原始stdin传播到cmd2dup2(0,4)在外部{...}dup2(4,0)内部还原为{...})。
斯特凡Chazelas

您可以使用它mktemp -d来确保可以获取FIFO,因为没有人应该写入您的全新随机临时目录。
Daniel H

@DanielH,我已经提到过mktemp -d。但这不是标准/ POSIX命令。
斯特凡Chazelas

didn,我没意识到。哎呀。快速搜索似乎表明大多数系统都支持它,因此它可能仍是可移植的,但不是POSIX。
Daniel H

-1

如果需要避免在有用的变量中丢失变量cmd | while read A B C,而不是:

VAR="before"
while read A B C 
do
  VAR="$A $VAR"
done < <(cmd)
echo "$VAR"

您可以使用:

VAR="before"
while read A B C 
do
  VAR="$A $VAR"
done << EndOfText
`cmd`
EndOfText
echo "$VAR"

所以要回答这个问题:

sort file1 | diff /dev/stdin /dev/stdout 2<<EOT
`sort file2`
EOT
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.