Sostituzione del processo e pipe

Mi chiedevo come capire quanto segue:

Condurre lo stdout di un comando nello stdin di un altro è una tecnica potente. Ma cosa succede se è necessario eseguire il pipe dello stdout di più comandi? È qui che entra in gioco la sostituzione del processo.

In altre parole, la sostituzione del processo può fare tutto ciò che pipe può fare?

Cosa può fare la sostituzione del processo, ma pipe no?

Answer

Un buon modo per fare la differenza tra loro è fare un po di esperimenti sulla riga di comando. Nonostante la somiglianza visiva nelluso del carattere <, fa qualcosa di molto diverso da un reindirizzamento o pipe.

Usiamo il date comando per il test.

$ date | cat Thu Jul 21 12:39:18 EEST 2011 

Questo è un esempio inutile ma mostra che cat ha accettato loutput di date su STDIN e lo ha risputato. Gli stessi risultati possono essere ottenuti con la sostituzione del processo:

$ cat <(date) Thu Jul 21 12:40:53 EEST 2011 

Tuttavia ciò che è appena accaduto dietro le quinte era diverso. Invece di ricevere uno stream STDIN, a cat è stato effettivamente passato il nome di un file che doveva essere aperto e leggi. Puoi vedere questo passaggio utilizzando echo invece di cat.

$ echo <(date) /proc/self/fd/11 

Quando cat ha ricevuto il nome del file, legge il contenuto del file per noi. Daltra parte, echo ci ha appena mostrato il nome del file che è stato passato. Questa differenza diventa più evidente se aggiungi più sostituzioni:

$ cat <(date) <(date) <(date) Thu Jul 21 12:44:45 EEST 2011 Thu Jul 21 12:44:45 EEST 2011 Thu Jul 21 12:44:45 EEST 2011 $ echo <(date) <(date) <(date) /proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13 

è possibile combinare la sostituzione del processo (che genera un file) e il reindirizzamento dellinput (che collega un file a STDIN):

$ cat < <(date) Thu Jul 21 12:46:22 EEST 2011 

Sembra più o meno lo stesso ma questa volta a cat è stato passato lo stream STDIN invece del nome di un file. Puoi vederlo provandolo con echo:

$ echo < <(date) <blank> 

Poiché echo non legge STDIN e nessun argomento è stato passato, non otteniamo nulla.

I pipe e i reindirizzamenti di input inseriscono il contenuto nel flusso STDIN. La sostituzione del processo esegue i comandi, salva il loro output in un file temporaneo speciale e quindi passa quel nome file al posto del comando. Qualunque comando tu stia usando lo considera come un nome di file. Tieni presente che il file creato non è un file normale ma una named pipe che viene rimossa automaticamente quando non è più necessaria.

Commenti

  • Se io inteso correttamente, tldp.org/LDP/abs/html/process-sub.html#FTN.AEN18244 afferma che la sostituzione del processo crea file temporanei, non pipe denominate. Per quanto ne so denominato non creare file temporanei. La scrittura sulla pipe non implica mai la scrittura su disco: stackoverflow.com/a/6977599/788700
  • So che questa risposta è legittima ‘ perché utilizza la parola grok : D
  • @Adobe puoi confermare se la sostituzione del processo del file temporaneo produce è una pipe denominata con: [[ -p <(date) ]] && echo true. Questo produce true quando lo eseguo con bash 4.4 o 3.2.

Answer

Ecco tre cose che puoi fare con la sostituzione del processo che altrimenti sarebbero impossibili.

Più input di processo

diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls) 

Lì semplicemente non è possibile farlo con le pipe.

Preservare STDIN

Supponi di avere quanto segue:

curl -o - http://example.com/script.sh #/bin/bash read LINE echo "You said ${LINE}!" 

E vuoi eseguirlo direttamente. Quanto segue fallisce miseramente. Bash sta già usando STDIN per leggere lo script, quindi altri input sono impossibili.

curl -o - http://example.com/script.sh | bash 

Ma in questo modo funziona perfettamente.

bash <(curl -o - http://example.com/script.sh) 

Sostituzione del processo in uscita

Si noti inoltre che la sostituzione del processo funziona anche nellaltro modo. Quindi puoi fare qualcosa del genere:

(ls /proc/*/exe >/dev/null) 2> >(sed -n \ "/Permission denied/ s/.*\(\/proc.*\):.*/\1/p" > denied.txt ) 

Questo “è un esempio un po contorto, ma invia stdout a /dev/null, mentre invii stderr a uno script sed per estrarre i nomi dei file per i quali un ” autorizzazione negata “, quindi invia QUESTI risultati a un file.

Nota che il primo comando e il reindirizzamento stdout sono tra parentesi ( subshell ) in modo che solo il risultato di QUEL comando venga inviato a /dev/null e non interferisca con il resto della riga.

Commenti

  • ‘ vale la pena notare che nel diff esempio potresti interessarti al caso in cui cd potrebbe non funzionare: diff <(cd /foo/bar/ && ls) <(cd /foo/baz && ls).
  • ” durante il piping stderr “: isn ‘ t il indicare che non si tratta di una tubazione, ma di un file fifo?
  • @Gauthier no; il comando viene sostituito non con un fifo ma con un riferimento al descrittore di file. Quindi ” echo < (echo) ” dovrebbe produrre qualcosa come ” / dev / fd / 63 “, che è un dispositivo a caratteri speciali che legge o scrive da FD numero 63.

Risposta

Suppongo che tu stia parlando di bash o di qualche altra shell avanzata, perché il posix shell non dispone della sostituzione del processo .

bash rapporti della pagina di manuale:

Sostituzione del processo
La sostituzione del processo è supportata sui sistemi che supportano le named pipe (FIFO) o il metodo / dev / fd di denominazione dei file aperti. Ha la forma di < (elenco) o> (elenco). La lista dei processi viene eseguita con il suo input o output connesso a un FIFO o ad un file in / dev / fd. Il nome di questo file viene passato come argomento al comando corrente come risultato dellespansione. Se viene utilizzato il modulo> (elenco), la scrittura nel file fornirà linput per lelenco. Se viene utilizzato il modulo < (list), il file passato come argomento dovrebbe essere letto per ottenere loutput di list.

Quando disponibile, processo di sostituzione viene eseguita simultaneamente con lespansione di parametri e variabili, la sostituzione dei comandi e lespansione aritmetica.

In altre parole, e da un punto di vista pratico, puoi usare unespressione come la seguente

<(commands) 

come nome file per altri comandi che richiedono un file come parametro. Oppure puoi usare il reindirizzamento per un file del genere:

while read line; do something; done < <(commands) 

Tornando alla tua domanda, mi sembra che la sostituzione del processo e le pipe non abbiano molto in comune.

Se vuoi reindirizzare in sequenza loutput di più comandi, puoi usare una delle seguenti forme:

(command1; command2) | command3 { command1; command2; } | command3 

ma tu può anche utilizzare il reindirizzamento alla sostituzione del processo

command3 < <(command1; command2) 

infine, se command3 accetta un parametro file (in sostituzione di stdin )

command3 <(command1; command2) 

Commenti

  • so < ( ) e < < () fa lo stesso effetto, giusto?
  • @solfish: not exacllty: the firse can essere utilizzato ovunque sia previsto un nome di file, il secondo è un reindirizzamento dellinput per quel nome di file

Risposta

Se un Il comando accetta un elenco di file come argomenti e li elabora come input (o output, ma non comunemente), ognuno di questi file può essere una pipe con nome o uno pseudo-file / dev / fd fornito in modo trasparente dalla sostituzione del processo:

$ sort -m <(command1) <(command2) <(command3) 

Questo “reindirizza” loutput dei tre comandi allordinamento, poiché lordinamento può richiedere un elenco di file di input sulla riga di comando.

Commenti

  • IIRC la sintassi del < (comando) è una funzione solo bash.
  • @Philomath: It ‘ è in Anche ZSH.
  • Bene, ZSH ha tutto … (o almeno ci prova).
  • @Philomath: come viene implementata la sostituzione del processo in altre shell?
  • @Philomath <(), come molte funzionalità avanzate della shell, era originariamente una funzionalità di ksh ed è stata adottata da bash e zsh. psub è specificamente una funzione di pesce, niente a che fare con POSIX.

Risposta

Va notato che la sostituzione del processo non è limitata alla forma <(command), che utilizza loutput di command come file. Può essere nella forma >(command) che alimenta un file anche come input per command. Questo è anche menzionato nella citazione del manuale di bash nella risposta di @enzotib “.

Per lesempio date | cat sopra, un comando che utilizza la sostituzione del processo del form >(command) per ottenere lo stesso effetto sarebbe

date > >(cat) 

Tieni presente che > prima che >(cat) sia necessario. Questo può essere nuovamente illustrato chiaramente da echo come nella risposta di @Caleb.

$ echo >(cat) /dev/fd/63 

Quindi, senza >, date >(cat) sarebbe il uguale a date /dev/fd/63 che stamperà un messaggio a stderr.

Supponi di avere un programma che accetta solo nomi di file come parametri e non elabora stdin o stdout.Userò lo script semplificato psub.sh per illustrarlo. Il contenuto di psub.sh è

#!/bin/bash [ -e "$1" -a -e "$2" ] && awk "{print $1}" "$1" > "$2" 

Fondamentalmente, verifica che entrambi i suoi argomenti siano file (non necessariamente regolari files) e, in questo caso, scrivi il primo campo di ogni riga da "$1" a "$2" utilizzando awk. Quindi, un comando che combina tutto ciò che è stato menzionato finora è

./psub.sh <(printf "a a\nc c\nb b") >(sort) 

Questo stamperà

a b c 

ed è equivalente a

printf "a a\nc c\nb b" | awk "{print $1}" | sort 

ma quanto segue non funzionerà e dobbiamo utilizzare la sostituzione del processo qui,

printf "a a\nc c\nb b" | ./psub.sh | sort 

o la sua forma equivalente

printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort 

Se ./psub.sh legge anche stdin oltre a quanto menzionato sopra, allora una forma simile non esiste, e in quel caso non cè niente che possiamo usare al posto della sostituzione del processo (ovviamente puoi anche usare una denominato pipe o file temporaneo, ma questa è unaltra storia).

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *