Apa cara portabel (POSIX) untuk mencapai substitusi proses?

25

Beberapa shell, seperti bash, mendukung Substitusi Proses yang merupakan cara untuk menampilkan output proses sebagai file, seperti ini:

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

Namun, konstruksi ini bukan POSIX dan, karenanya, tidak portabel. Bagaimana bisa memproses penggantian dicapai dalam POSIX cara-ramah (yaitu satu yang bekerja di /bin/sh) ?

catatan: pertanyaannya bukan menanyakan bagaimana membedakan dua file yang diurutkan - itu hanya contoh yang dibuat untuk menunjukkan substitusi proses !

starfry
sumber
mywiki.wooledge.org/ProcessSubstitution memiliki catatan bahwa mywiki.wooledge.org/NamedPipes dapat digunakan ..
Sundeep
2
Lihat juga unix.stackexchange.com/a/43536/117549
Jeff Schaller
1
POSIX tidak perlu /bin/shmenjadi shell POSIX, hanya saja ada perintah yang disebut di shsuatu tempat yang di lingkungan yang benar adalah POSIX compliant. Misalnya, pada Solaris 10, /bin/shbukan shell POSIX, itu adalah shell Bourne kuno dan shell POSIX ada di /usr/xpg4/bin/sh.
Stéphane Chazelas

Jawaban:

17

Fitur itu diperkenalkan oleh ksh(pertama kali didokumentasikan dalam ksh86) dan memanfaatkan /dev/fd/nfitur tersebut (ditambahkan secara independen di beberapa BSD dan sistem AT&T sebelumnya). Di kshdan hingga ksh93u, itu tidak akan berfungsi kecuali sistem Anda memiliki dukungan untuk / dev / fd / n. zsh, bash dan ksh93u+dan di atas dapat menggunakan pipa bernama sementara (pipa bernama ditambahkan di SysIII saya percaya) di mana / dev / fd / n tidak tersedia.

Pada sistem di mana tersedia (POSIX tidak menentukan itu), Anda dapat melakukan sendiri proses substitusi ( ) dengan:/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

Namun itu tidak bekerja dengan ksh93di Linux seperti di sana, pipa shell diimplementasikan dengan socketpairs bukan pipa dan membuka di /dev/fd/3mana fd 3 menunjuk ke soket tidak bekerja di Linux.

Meskipun POSIX tidak menentukan . Itu menentukan pipa bernama. Pipa bernama bekerja seperti pipa normal kecuali Anda dapat mengaksesnya dari sistem file. Masalahnya di sini adalah bahwa Anda harus membuat yang sementara dan membersihkan yang sulit dilakukan dengan andal terutama mengingat bahwa POSIX tidak memiliki mekanisme standar (seperti yang ditemukan pada beberapa sistem) untuk membuat file atau direktori sementara, dan melakukan penanganan sinyal dengan mudah. (untuk membersihkan pada saat hang-up atau membunuh) juga sulit dilakukan dengan mudah./dev/fd/nmktemp -d

Anda dapat melakukan sesuatu seperti:

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"

(tidak mengurus penanganan sinyal di sini).

Stéphane Chazelas
sumber
Contoh pipa bernama benar-benar jelas bagi saya (saya kira 10 adalah batas sewenang-wenang?) Tapi saya tidak tahu /dev/fd/ncontoh Anda . Mengapa deskriptor 4 ditutup dua kali? (Dan juga deskriptor 3.) Saya tersesat.
Wildcard
@Wildcard, ditutup untuk perintah yang tidak membutuhkannya. Di sini, hanya kebutuhan diff yang fd 3. Tidak perlu fd 4, itu hanya digunakan untuk menyebarkan stdin asli ke cmd2( dup2(0,4)di luar {...}, dikembalikan dengan dup2(4,0)untuk di dalam {...}).
Stéphane Chazelas
Anda dapat menggunakan mktemp -duntuk membantu memastikan Anda mendapatkan FIFO, karena tidak seorang pun harus menulis ke direktori sementara acak baru Anda.
Daniel H
@DanielH, saya sudah menyebutkan mktemp -d. Tapi itu bukan perintah standar / POSIX.
Stéphane Chazelas
Hah, saya tidak menyadarinya. Ups. Pencarian cepat tampaknya menunjukkan bahwa sebagian besar sistem mendukungnya, jadi mungkin masih portabel, tetapi bukan POSIX.
Daniel H
-1

Jika perlu untuk menghindari variabel yang hilang dalam berguna cmd | while read A B C, alih-alih:

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

Anda dapat gunakan:

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

Jadi untuk menjawab pertanyaan:

sort file1 | diff /dev/stdin /dev/stdout 2<<EOT
`sort file2`
EOT
defdefred
sumber