透過檔案描述符改組和 /dev/fd 的兩個輸入管道

透過檔案描述符改組和 /dev/fd 的兩個輸入管道

我想將兩個程式合而為一。如果我的 shell 支持,我可以使用工藝替代。例如,要以無關順序列出兩個文件的公共行,我可以使用

comm -12 <(sort a) <(sort b)

然而,在 plain 中不存在進程替換sh。我可以透過創建一個完整的 POSIX 可移植性來做到這一點命名管道,但這很麻煩,因為它需要找到 FIFO 的目錄並隨後進行清理。一個很好的實用折衷方案是使用兩個 shell 管道結構並使用檔案描述符改組將一個管道移動到另一個檔案描述符,然後使用/dev/fd來指定管道,這適用於大多數 Unix 變體:

sort a | { exec 3<&0; sort b | comm -12 /dev/fd/0 /dev/fd/3; }

這適用於 dash、bash、BusyBox sh 等,但不適用於 ksh93 和 mksh。為什麼?

$ mksh -c 'sort a | { exec 3<&0; sort b | comm -12 /dev/fd/0 /dev/fd/3; }'
$ ksh93 -c 'sort a | { exec 3<&0; sort b | comm -12 /dev/fd/0 /dev/fd/3; }'
comm: /dev/fd/0: No such device or address

答案1

與其他指令上的重新導向不同,exec當 shell 執行外部程式時,內建指令上的重新導向可能會關閉。POSIX 允許這兩種行為。 Ksh(ATT ksh、pdksh 和 mksh)在執行外部實用程式時關閉這些描述符(即,對於exec內建程式上的重定向,在呼叫dup2執行重定向後,它們FD_CLOEXEC在新描述符上設定標誌)。 Bourne shell、dash、bash、zsh 和 BusyBox sh 將此重定向視為任何其他重定向。

解決雙輸入管道問題的一種更可移植的解決方案(假設存在/dev/fd)是對讀取輸入的命令執行另一次重定向,將檔案描述符移至新的檔案描述符。此額外的重定向不會在新描述符上設定 close-on-exec 標誌。

sort a | { exec 3<&0; sort b | comm -12 /dev/fd/0 /dev/fd/4 4<&3; }

這適用於 pdksh/mksh 和 ksh93r,但不適用於最新版本的 ksh(93s+ 2008-01-31 或 93u+ 2012-08-01)。我不明白 ksh 在那裡做什麼。

相關內容