Come emulare la sostituzione del processo in Dash?


18

In bash, posso usare Process Substitution e trattare l'output di un processo come se fosse un file salvato su disco:

$ echo <(ls)
/dev/fd/63

$ ls -lAhF <(ls)
lr-x------ 1 root root 64 Sep 17 12:55 /dev/fd/63 -> pipe:[1652825]

sfortunatamente, Sostituzione processo non è supportata in dash.

Quale sarebbe il modo migliore per emulare Process Substitutionin dash?

Non voglio salvare l'output come file temporaneo da qualche parte ( /tmp/) e quindi eliminarlo. C'è un modo alternativo?


A seconda di cosa stai cercando di fare, potresti essere in grado di usare le pipe.
Eric Renouf,

Onestamente, se la portabilità non è la tua preoccupazione principale qui non sarebbe più semplice installarla bashsul tuo dispositivo?
Arkadiusz Drabczyk,

1
L'esempio fornito nell'avviso di bontà sembra essere l'oggetto di questa risposta collegata . Come mostrato qui, potrebbe essere una versione semplificata della risposta di Gilles (ipotizzando la disponibilità /dev/fde l'utilizzo xz -cd <file>anziché cat <file> | xz -d) xz -cd "$1" | { xz -cd "$2" | { diff /dev/fd/3 /dev/fd/4; } 3<&0; } 4<&0.
fra-san,

1
@ fra-san - questo è quello che mi serviva. Puoi farne una risposta se vuoi. Grazie.
Martin Vegter,

Risposte:


4

La domanda nell'attuale avviso di bontà:

l'esempio generale è troppo complicato. Qualcuno può spiegare come implementare il seguente esempio?diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)

sembra avere una risposta qui .

Come mostrato nella risposta di Gilles , l'idea generale è quella di inviare l'output dei comandi "producer" ai nuovi file del dispositivo 1 nelle diverse fasi di una pipeline, rendendoli disponibili per i comandi "consumer", che potrebbero eventualmente assumere i nomi dei file come argomenti ( supponendo che il tuo sistema ti dia accesso a descrittori di file come /dev/fd/X).

Il modo più semplice per ottenere ciò che stai cercando è probabilmente:

xz -cd file1.xz | { xz -cd file2.xz | diff /dev/fd/3 -; } 3<&0

(Usando file1.xzal posto di "$1"per leggibilità e xz -cdanziché cat ... | xz -dperché è sufficiente un singolo comando).

L'output del primo comando "producer" xz -cd file1.xz, viene reindirizzato a un comando composto ( {...}); ma, invece di essere consumato immediatamente come input standard del comando successivo, viene duplicato nel descrittore di file 3e quindi reso accessibile a tutto all'interno del comando composto come /dev/fd/3. L'output del secondo comando "producer" xz -cd file2.xz, che non utilizza né l'input standard né alcunché dal descrittore di file 3, viene quindi reindirizzato al comando "consumer" diff, che legge dal suo input standard e da /dev/fd/3.

È possibile aggiungere duplicazioni di descrittori di file e tubazioni per fornire file di dispositivo per tutti i comandi "produttore" necessari, ad esempio:

xz -cd file1.xz | { xz -cd file2.xz | { diff /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0

Sebbene possa essere irrilevante nel contesto della tua domanda specifica, vale la pena notare che:

  1. cmd1 <(cmd2) <(cmd3), cmd2 | { cmd3 | { cmd1 /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0E ( cmd2 | ( cmd3 | ( cmd1 /dev/fd/3 /dev/fd/4 ) 4<&0 ) 3<&0 )hanno diversi effetti potenziali sull'ambiente di esecuzione iniziale.

  2. Contrariamente a quanto accade in cmd1 <(cmd2) <(cmd3), cmd3e cmd1in cmd2 | { cmd3 | { cmd1 /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0non sarà in grado di leggere alcun input dall'utente. Ciò richiederà ulteriori descrittori di file. Ad esempio, per abbinare

    diff <(echo foo) <(read var; echo "$var")
    

    avrai bisogno di qualcosa del genere

    { echo foo | { read var 0<&9; echo "$var" | diff /dev/fd/3 -; } 3<&0; } 9<&0
    

1 Altre informazioni su di esse sono disponibili in U&L, ad es. In Informazioni / sviluppo e relativi sottodirectory e file .


12

Puoi riprodurre ciò che fa il guscio sotto il cofano facendo manualmente l'impianto idraulico. Se il tuo sistema ha voci, puoi usare il riordino del descrittore di file: puoi tradurre/dev/fd/NNN

main_command <(produce_arg1) <(produce_arg2) >(consume_arg3) >(consume_arg4)

per

{ produce_arg1 |
  { produce_arg2 |
    { main_command /dev/fd5 /dev/fd6 /dev/fd3 /dev/fd4 </dev/fd/8 >/dev/fd/9; } 5<&0 3>&1 |
    consume_arg3; } 6<&0 4>&1; |
  consume_arg4; } 8<&0 9>&1

Ho mostrato un esempio più complesso per illustrare più ingressi e uscite. Se non è necessario leggere dall'input standard e l'unica ragione per cui si sta utilizzando la sostituzione del processo è che il comando richiede un nome file esplicito, è possibile utilizzare semplicemente /dev/stdin:

main_command <(produce_arg1)
produce_arg1 | main_command /dev/stdin

Senza , è necessario utilizzare una pipa denominata . Una pipe denominata è una voce della directory, quindi è necessario creare un file temporaneo da qualche parte, ma quel file è solo un nome, non contiene alcun dato./dev/fd/NNN

tmp=$(mktemp -d)
mkfifo "$tmp/f1" "$tmp/f2" "$tmp/f3" "$tmp/f4"
produce_arg1 >"$tmp/f1" &
produce_arg2 >"$tmp/f2" &
consume_arg3 <"$tmp/f3" &
consume_arg4 <"$tmp/f4" &
main_command "$tmp/f1" "$tmp/f2" "$tmp/f3" "$tmp/f4"
rm -r "$tmp"

2
</dev/fd/8 >/dev/fd/9non sono equivalenti a quelli <&8 >&9su Linux e dovrebbero essere evitati lì.
Stéphane Chazelas,

@Gilles - Sono confuso dal complesso esempio. La prego di mostrare come emulare: diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)?
Martin Vegter,

3

Qualcuno può spiegare come implementare il seguente esempio?
diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)

Penso che i named pipe siano più semplici da grok dei reindirizzamenti, quindi in termini più semplici:

mkfifo p1 p2               # create pipes
cat "$2" | xz -d > p1 &    # start the commands in the background
cat "$1" | xz -d > p2 &    #    with output to the pipes
diff p1 p2                 # run 'diff', reading from the pipes
rm p1 p2                   # remove them at the end

p1e p2sono pipe con nome temporaneo, potrebbero essere chiamate con qualsiasi nome.

Sarebbe più intelligente creare le pipe /tmp, ad esempio in una directory come mostra la risposta di Gilles , e nota che non è necessario catqui, quindi:

tmpdir=$(mktemp -d)
mkfifo "$tmpdir/p1" "$tmpdir/p2"
xz -d < "$2" > "$tmpdir/p1" &
xz -d < "$1" > "$tmpdir/p2" &
diff "$tmpdir/p1" "$tmpdir/p2"
rm -r "$tmpdir"

(Probabilmente potresti anche scappare senza le virgolette, poiché mktempè probabile che crei nomi di file "simpatici".)

La soluzione pipe ha lo spunto che se il comando principale ( diff) muore prima di leggere tutti gli input, i processi in background possono essere lasciati in sospeso.


0

Che dire:

cat "$2" | xz -d | diff /dev/sdtin /dev/stderr 2<<EOT
`cat "$1" | xz -d`
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.