Shell

實現程序替換的可移植(POSIX)方式是什麼?

  • March 17, 2021

一些 shell,如bash,支持程序替換,這是一種將程序輸出呈現為文件的方法,如下所示:

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

但是,此構造不是POSIX,因此不可移植。如何以一種POSIX友好的方式(即在 中工作的方式)實現程序替換/bin/sh

注意:問題不是問如何區分兩個排序的文件——這只是一個人為的例子來展示程序替換

該功能由ksh(首先在 ksh86 中記錄)引入並正在使用該功能(之前在某些 BSD 和 AT&T 系統中獨立添加)。在ksh93u 及之前,除非您的系統支持. zsh、bash 及以上版本可以在不可用的地方使用臨時命名管道(系統 III 中添加的命名管道)。/dev/fd/*n*``ksh``/dev/fd/*n*``ksh93u+``/dev/fd/*n*

在可用的系統上(POSIX 未指定這些),您可以自己進行程序替換(例如,):/dev/fd/*n*``diff <(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/*n*``mktemp -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

(這裡不處理信號處理)。

引用自:https://unix.stackexchange.com/questions/309547