Qual'è la differenza tra <<
, <<<
e < <
in bash?
Qual'è la differenza tra <<
, <<<
e < <
in bash?
Risposte:
Qui documento
<<
è noto come here-document
struttura. Fai sapere al programma quale sarà il testo finale e ogni volta che viene visualizzato quel delimitatore, il programma leggerà tutte le cose che hai dato al programma come input ed eseguirà un'attività su di esso.
Ecco cosa intendo:
$ wc << EOF
> one two three
> four five
> EOF
2 5 24
In questo esempio diciamo wc
al programma di attendere la EOF
stringa, quindi digitare cinque parole e quindi digitare EOF
per segnalare che abbiamo finito di dare l'input. In effetti, è simile a correre wc
da solo, digitare parole, quindi premereCtrlD
In bash questi sono implementati tramite file temporanei, di solito nella forma /tmp/sh-thd.<random string>
, mentre in dash sono implementati come pipe anonime. Ciò può essere osservato tramite la traccia delle chiamate di sistema con strace
comando. Sostituisci bash
con sh
per vedere come /bin/sh
esegue questo reindirizzamento.
$ strace -e open,dup2,pipe,write -f bash -c 'cat <<EOF
> test
> EOF'
Qui stringa
<<<
è noto come here-string
. Invece di digitare il testo, si fornisce una stringa di testo predefinita a un programma. Ad esempio, con il programma bc
che possiamo fare bc <<< 5*4
per ottenere l'output per quel caso specifico, non è necessario eseguire bc in modo interattivo.
Le stringhe qui in bash sono implementate tramite file temporanei, di solito nel formato /tmp/sh-thd.<random string>
, che in seguito vengono scollegati, facendo in modo che occupino temporaneamente spazio di memoria ma non vengano visualizzati nell'elenco delle /tmp
voci della directory, e che esistano effettivamente come file anonimi, che potrebbero comunque viene fatto riferimento tramite il descrittore di file dalla shell stessa e quel descrittore di file viene ereditato dal comando e successivamente duplicato sul descrittore di file 0 (stdin) tramite la dup2()
funzione. Questo può essere osservato tramite
$ ls -l /proc/self/fd/ <<< "TEST"
total 0
lr-x------ 1 user1 user1 64 Aug 20 13:43 0 -> /tmp/sh-thd.761Lj9 (deleted)
lrwx------ 1 user1 user1 64 Aug 20 13:43 1 -> /dev/pts/4
lrwx------ 1 user1 user1 64 Aug 20 13:43 2 -> /dev/pts/4
lr-x------ 1 user1 user1 64 Aug 20 13:43 3 -> /proc/10068/fd
E tramite traccia di syscalls (output abbreviato per leggibilità; nota come il file temporaneo viene aperto come fd 3, i dati scritti su di esso, quindi viene riaperto con O_RDONLY
flag come fd 4 e successivamente scollegato, quindi dup2()
su fd 0, che è ereditato da cat
più tardi ):
$ strace -f -e open,read,write,dup2,unlink,execve bash -c 'cat <<< "TEST"'
execve("/bin/bash", ["bash", "-c", "cat <<< \"TEST\""], [/* 47 vars */]) = 0
...
strace: Process 10229 attached
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
[pid 10229] write(3, "TEST", 4) = 4
[pid 10229] write(3, "\n", 1) = 1
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDONLY) = 4
[pid 10229] unlink("/tmp/sh-thd.uhpSrD") = 0
[pid 10229] dup2(4, 0) = 0
[pid 10229] execve("/bin/cat", ["cat"], [/* 47 vars */]) = 0
...
[pid 10229] read(0, "TEST\n", 131072) = 5
[pid 10229] write(1, "TEST\n", 5TEST
) = 5
[pid 10229] read(0, "", 131072) = 0
[pid 10229] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=10229, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
Opinione: potenzialmente perché qui le stringhe fanno uso di file di testo temporanei, è la possibile ragione per cui le stringhe qui inseriscono sempre una nuova riga finale, poiché il file di testo per definizione POSIX deve avere linee che terminano con un carattere di nuova riga.
Sostituzione del processo
Come spiega tldp.org ,
La sostituzione del processo alimenta l'output di un processo (o processi) nello stdin di un altro processo.
Quindi in effetti questo è simile al piping stdout di un comando all'altro, ad es echo foobar barfoo | wc
. Ma nota: nella manpage di bash vedrai che è indicato come <(list)
. Quindi in pratica puoi reindirizzare l'output di più comandi (!).
Nota: tecnicamente quando dici < <
che non ti riferisci a una cosa, ma a due reindirizzamenti con reindirizzamento singolo <
e di processo dell'output da <( . . .)
.
Ora cosa succede se elaboriamo solo la sostituzione?
$ echo <(echo bar)
/dev/fd/63
Come puoi vedere, la shell crea un descrittore di file temporaneo /dev/fd/63
dove va l'output (che secondo la risposta di Gilles , è una pipe anonima). Ciò significa <
che reindirizza quel descrittore di file come input in un comando.
Quindi un esempio molto semplice potrebbe essere la sostituzione di processo dell'output da due comandi echo in wc:
$ wc < <(echo bar;echo foo)
2 2 8
Quindi qui facciamo in modo che la shell crei un descrittore di file per tutto l'output che avviene tra parentesi e reindirizziamo quello come input a wc
. e opportunamente abbiamo 2 parole, 2 righe e 6 caratteri più due nuove righe contate.
Nota a margine : la sostituzione del processo può essere definita bashism (un comando o una struttura utilizzabile in shell avanzate come bash
, ma non specificato da POSIX), ma è stata implementata ksh
prima dell'esistenza di bash come pagina man di ksh e questa risposta suggerisce. Le conchiglie gradiscono tcsh
e mksh
tuttavia non hanno una sostituzione di processo. Quindi, come potremmo fare per reindirizzare l'output di più comandi in un altro comando senza sostituzione del processo? Raggruppamento più tubazioni!
$ (echo foo;echo bar) | wc
2 2 8
In effetti questo è lo stesso dell'esempio precedente, tuttavia, questo è diverso dalla sostituzione del processo, dal momento che facciamo stdout dell'intera subshell e stdin di wc
collegati con la pipe . D'altra parte, la sostituzione del processo fa leggere un comando a un descrittore di file temporaneo.
Quindi, se possiamo fare il raggruppamento con le tubazioni, perché abbiamo bisogno della sostituzione del processo? Perché a volte non possiamo usare le tubazioni. Considera l'esempio di seguito: confrontare gli output di due comandi con diff
(che ha bisogno di due file, e in questo caso gli stiamo dando due descrittori di file)
diff <(ls /bin) <(ls /usr/bin)
< <
viene usato quando si ottiene lo stdin da una sostituzione di processo . Questo comando potrebbe essere simile: cmd1 < <(cmd2)
. Ad esempio,wc < <(date)
< <
non è una cosa da sola, nel caso della sostituzione del processo è solo <
seguito da qualcos'altro che inizia con<
<<<
stato inizialmente implementato dalla porta Unix della shell Plan 9 rc, successivamente adottata da zsh, bash e ksh93. Non lo definirei un bashismo.
echo 'foo' | read; echo ${REPLY}
sarà non ritorno foo
, perché read
è iniziato in un sub-shell - tubazione inizia un sub-shell. Tuttavia, read < <(echo 'foo'); echo ${REPLY}
restituisce correttamente foo
, poiché non è presente alcuna shell secondaria.
< <
è un errore di sintassi:
$ cat < <
bash: syntax error near unexpected token `<'
< <()
è la sostituzione di processo ( <()
) combinata con il reindirizzamento ( <
):
Un esempio inventato:
$ wc -l < <(grep ntfs /etc/fstab)
4
$ wc -l <(grep ntfs /etc/fstab)
4 /dev/fd/63
Con la sostituzione del processo, il percorso del descrittore di file viene utilizzato come un nome file. Nel caso in cui non si desideri (o non si possa) utilizzare direttamente un nome file, si combina la sostituzione del processo con il reindirizzamento.
Per essere chiari, non esiste un < <
operatore.
<()
dà una cosa simile a un nome di file, quindi è più generalmente utile - < <()
sta sostituendo lo stdin dove potrebbe non essere necessario. In wc
quest'ultimo sembra essere più utile. Potrebbe essere meno utile altrove
< <
è un errore di sintassi, probabilmente intendi command1 < <( command2 )
quale è un semplice reindirizzamento dell'input seguito da una sostituzione del processo ed è molto simile ma non equivalente a:
command2 | command1
La differenza supponendo che si stia eseguendo bash
è command1
eseguita in una subshell nel secondo caso mentre è eseguita nella shell corrente nel primo. Ciò significa che le variabili impostate command1
non andrebbero perse con la variante di sostituzione del processo.
< <
darà un errore di sintassi. L'uso corretto è il seguente:
Spiegare con l'aiuto di esempi:
Esempio per < <()
:
while read line;do
echo $line
done< <(ls)
Nell'esempio sopra, l'input per il ciclo while proviene dal ls
comando che può essere letto riga per riga ed echo
ed nel ciclo.
<()
viene utilizzato per la sostituzione del processo. Maggiori informazioni ed esempi per <()
sono disponibili a questo link: