实现进程替换的可移植(POSIX)方式是什么?

实现进程替换的可移植(POSIX)方式是什么?

一些 shell,例如bash,支持流程替代这是一种将进程输出呈现为文件的方法,如下所示:

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

然而,这个构造并不是POSIX因此,不便于携带。如何实现工艺替代POSIX- 友好的态度(即在 中工作的/bin/sh

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

答案1

该功能是由ksh(首先在 ksh86 中记录)引入的,并且正在使用该功能(之前在一些 BSD 和 AT&T 系统中独立添加)。在ksh93u 及之前版本中,除非您的系统支持. zsh、bash 及以上版本可以在不可用的情况下使用临时命名管道(System III 中添加的命名管道) 。/dev/fd/nksh/dev/fd/nksh93u+/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

然而,这在 Linux 上不起作用ksh93,因为 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"

cleanup

(这里不考虑信号处理)。

答案2

您可以依靠heredoc和/dev/fd/n文件来实现这一点。例如,bash您可以这样做:

#!/usr/bin/env bash

paste  <(echo "$SHELL") <(echo "$TERM") <(echo "$LANG")

但在 POSIX sh 中,你必须这样做:

#!/bin/sh

paste /dev/fd/3 3<<-EOF /dev/fd/4 4<<-EOF /dev/fd/5 5<<-EOF 
$SHELL
EOF
$TERM
EOF
$LANG
EOF

输出是相同的。

为了回答这个问题,这是有效的:

#!/bin/sh

diff /dev/fd/3 3<<-EOF /dev/fd/4 4<<-EOF
$(sort file1)
EOF
$(sort file2)
EOF

它看起来很笨重,但是很有效。

答案3

如果需要避免丢失有用的变量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

相关内容