Jaki jest przenośny (POSIX) sposób na zastąpienie procesu?

25

Niektóre powłoki bashobsługują proces podstawiania, który jest sposobem prezentacji wyników procesu w postaci pliku:

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

Jednak ten konstrukt nie jest POSIX i dlatego nie jest przenośny. Jak można zastąpić proces w sposób przyjazny dla POSIX (tj. Taki, który działa /bin/sh) ?

uwaga: pytanie nie pyta, jak odróżnić dwa posortowane pliki - to tylko wymyślony przykład pokazujący podstawienie procesu !

rozgwiazdy
źródło
mywiki.wooledge.org/ProcessSubstitution ma notatkę, że mywiki.wooledge.org/NamedPipes mogą być używane ..
Sundeep
2
Zobacz także unix.stackexchange.com/a/43536/117549
Jeff Schaller
1
POSIX nie wymaga /bin/shbycia powłoką POSIX, wystarczy, że zostanie wywołane polecenie, shktóre w odpowiednim środowisku jest zgodne z POSIX. Na przykład /bin/shw systemie Solaris 10 nie jest powłoką POSIX, jest to starożytna powłoka Bourne'a, w której znajduje się powłoka POSIX /usr/xpg4/bin/sh.
Stéphane Chazelas,

Odpowiedzi:

17

Ta funkcja została wprowadzona przez ksh(po raz pierwszy udokumentowana w ksh86) i korzystała z tej /dev/fd/nfunkcji (dodana niezależnie w niektórych systemach BSD i AT&T wcześniej). W kshwersjach ksh93u i nowszych nie działałoby, gdyby Twój system nie obsługiwał / dev / fd / n. Zsh, bash i ksh93u+wyżej mogą korzystać z tymczasowych nazwanych potoków (uważam nazwane potoki dodane w SysIII), gdzie / dev / fd / n nie są dostępne.

W systemach, w których jest dostępny (POSIX ich nie określa), możesz samodzielnie dokonać procesu podstawienia ( ):/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

Jednak to nie działa ksh93w systemie Linux, ponieważ potoki powłoki są implementowane za pomocą par /dev/fd/3gniazd zamiast potoków i otwierania, w którym fd 3 wskazuje gniazdo nie działa w systemie Linux.

Chociaż POSIX nie określa . Określa nazwane potoki. Nazwane potoki działają jak normalne potoki, z wyjątkiem tego, że można uzyskać do nich dostęp z systemu plików. Problem polega na tym, że musisz utworzyć pliki tymczasowe i wyczyścić je później, co jest trudne do wykonania w sposób niezawodny, szczególnie biorąc pod uwagę, że POSIX nie ma standardowego mechanizmu (takiego jak w niektórych systemach) do tworzenia plików tymczasowych lub katalogów i przenoszenia sygnałów w sposób przenośny (sprzątanie po rozłączeniu lub zabiciu) jest również trudne do przeniesienia./dev/fd/nmktemp -d

Możesz zrobić coś takiego:

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"

(nie zajmując się tutaj obsługą sygnałów).

Stéphane Chazelas
źródło
Nazwany przykład potoku jest dla mnie całkowicie jasny (wydaje mi się, że 10 to arbitralny limit?), Ale nie mogę zrozumieć twojego /dev/fd/nprzykładu. Dlaczego deskryptor 4 jest zamykany dwukrotnie? (I tak jest z deskryptorem 3.) Zgubiłem się.
Wildcard,
@Wildcard, jest zamknięty dla poleceń, które go nie potrzebują. Tutaj tylko diff potrzebuje fd 3. Żadne nie potrzebuje fd 4, służy tylko do propagacji oryginalnego stdin cmd2( dup2(0,4)na zewnętrznym {...}, przywróconym dup2(4,0)do wewnątrz {...}).
Stéphane Chazelas
Możesz mktemp -dpomóc w uzyskaniu FIFO, ponieważ nikt nie powinien pisać w twoim nowym losowym katalogu tymczasowym.
Daniel H
@DanielH, już wspomniałem mktemp -d. Ale to nie jest standardowe polecenie / POSIX.
Stéphane Chazelas
Huh, nie zdawałem sobie z tego sprawy. Ups Szybkie wyszukiwanie wydaje się wskazywać, że większość systemów go obsługuje, więc może nadal być przenośne, ale nie jest to POSIX.
Daniel H
-1

W razie potrzeby, aby uniknąć utraty zmiennej użytecznej cmd | while read A B C, zamiast:

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

możesz użyć:

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

Aby odpowiedzieć na pytanie:

sort file1 | diff /dev/stdin /dev/stdout 2<<EOT
`sort file2`
EOT
defdefred
źródło