Qual è il modo portatile (POSIX) per ottenere la sostituzione del processo?


25

Alcune shell, come bash, supportano la sostituzione del processo, che è un modo per presentare l'output del processo come file, come questo:

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

Tuttavia, questo costrutto non è POSIX e, quindi, non portatile. Come si può ottenere la sostituzione del processo in modo compatibile con POSIX (ovvero uno che funziona /bin/sh) ?

nota: la domanda non è come diffondere due file ordinati - questo è solo un esempio inventato per dimostrare la sostituzione del processo !




1
POSIX non richiede /bin/shdi essere una shell POSIX, solo che esiste un comando chiamato shda qualche parte che nel giusto ambiente è conforme a POSIX. Ad esempio, su Solaris 10 /bin/shnon è una shell POSIX, è un'antica shell Bourne e la shell POSIX è inserita /usr/xpg4/bin/sh.
Stéphane Chazelas,

Risposte:


17

Quella funzione è stata introdotta da ksh(documentata per la prima volta in ksh86) e la utilizzava /dev/fd/n(aggiunta in modo indipendente in alcuni sistemi BSD e AT&T in precedenza). In kshe fino a ksh93u, non funzionerebbe se il tuo sistema non avesse il supporto per / dev / fd / n. zsh, bash e ksh93u+sopra possono usare pipe nominate temporanee (credo che vengano aggiunte pipe nominate nel SysIII) dove / dev / fd / n non sono disponibili.

Sui sistemi in cui è disponibile (POSIX non specifica quelli), è possibile eseguire personalmente la sostituzione ( ) con:/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

Tuttavia, ciò non funziona ksh93su Linux in quanto lì, i pipe shell sono implementati con socket al posto dei pipe e l'apertura in /dev/fd/3cui fd 3 punta a un socket non funziona su Linux.

Sebbene POSIX non specifichi . Specifica le pipe con nome. Le pipe con nome funzionano come normali pipe tranne per il fatto che è possibile accedervi dal file system. Il problema qui è che devi crearne di temporanei e ripulirli in seguito, il che è difficile da fare in modo affidabile soprattutto considerando che POSIX non ha meccanismi standard (come quelli che si trovano su alcuni sistemi) per creare file o directory temporanei e fare portabilità del segnale in modo portabile (ripulire dopo aver riagganciato o ucciso) è anche difficile da fare in modo portabile./dev/fd/nmktemp -d

Potresti fare qualcosa del tipo:

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"

(non occupandosi della gestione del segnale qui).


L'esempio di pipe denominato mi è completamente chiaro (immagino che 10 sia un limite arbitrario?) Ma non riesco a capire il tuo /dev/fd/nesempio. Perché il descrittore 4 viene chiuso due volte? (E così è il descrittore 3.) Mi sono perso.
Carattere jolly

@Wildcard, è chiusa per i comandi che non ne hanno bisogno. Qui, solo diff ha bisogno di fd 3. Nessuno ha bisogno di fd 4, viene utilizzato solo per propagare lo stdin originale cmd2( dup2(0,4)all'esterno {...}, ripristinato con dup2(4,0)per l'interno {...}).
Stéphane Chazelas

È possibile utilizzare mktemp -dper assicurarsi di poter ottenere FIFO, poiché nessuno dovrebbe scrivere nella nuova directory temporanea casuale nuova di zecca.
Daniel H

@DanielH, ho già menzionato mktemp -d. Ma questo non è un comando standard / POSIX.
Stéphane Chazelas,

Eh, non me ne sono reso conto. Ops. La ricerca rapida sembra mostrare che la maggior parte dei sistemi lo supporta, quindi potrebbe essere ancora portatile, ma non è POSIX.
Daniel H,

-1

Se necessario per evitare la variabile persa nell'utile cmd | while read A B C, anziché:

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

Puoi usare:

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

Quindi, per rispondere alla domanda:

sort file1 | diff /dev/stdin /dev/stdout 2<<EOT
`sort file2`
EOT
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.