Qual è la differenza tra <<, <<< e <<in bash?


103

Qual'è la differenza tra <<, <<<e < <in bash?


20
Almeno in questi tempi incentrati su Google, è difficile cercare questi operatori basati su simboli. Esiste un motore di ricerca in cui è possibile collegare "<< <<< <<" e ottenere qualcosa di utile?
Daniel Griscom,

11
@DanielGriscom C'è SymbolHound .
Dennis,

1
@DanielGriscom Stack Exchange supportava la ricerca di simboli, ma poi qualcosa si è rotto e nessuno l'ha mai riparato.
Muru,

Risposte:


116

Qui documento

<<è noto come here-documentstruttura. 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 wcal programma di attendere la EOFstringa, quindi digitare cinque parole e quindi digitare EOFper segnalare che abbiamo finito di dare l'input. In effetti, è simile a correre wcda 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 stracecomando. Sostituisci bashcon shper vedere come /bin/shesegue 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 bcche possiamo fare bc <<< 5*4per 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 /tmpvoci 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_RDONLYflag come fd 4 e successivamente scollegato, quindi dup2()su fd 0, che è ereditato da catpiù 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/63dove 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 kshprima dell'esistenza di bash come pagina man di ksh e questa risposta suggerisce. Le conchiglie gradiscono tcshe mkshtuttavia 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)

7
< <viene usato quando si ottiene lo stdin da una sostituzione di processo . Questo comando potrebbe essere simile: cmd1 < <(cmd2). Ad esempio,wc < <(date)
Giovanni 1024,

4
Sì, la sostituzione del processo è supportata da bash, zsh e AT&T ksh {88,93} ma non da pdksh / mksh.
Giovanni 1024,

2
< < non è una cosa da sola, nel caso della sostituzione del processo è solo <seguito da qualcos'altro che inizia con<
user253751

1
@muru Per quanto ne so, è <<<stato inizialmente implementato dalla porta Unix della shell Plan 9 rc, successivamente adottata da zsh, bash e ksh93. Non lo definirei un bashismo.
jlliagre,

3
Un altro esempio in cui non è possibile utilizzare tubazioni: 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.
Paddy Landau,

26

< < è 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.


ricevo dalla tua risposta che <<() più utile di <() giusto?
solfish

1
@solfish <()dà una cosa simile a un nome di file, quindi è più generalmente utile - < <()sta sostituendo lo stdin dove potrebbe non essere necessario. In wcquest'ultimo sembra essere più utile. Potrebbe essere meno utile altrove
muru

12

< <è 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è command1eseguita in una subshell nel secondo caso mentre è eseguita nella shell corrente nel primo. Ciò significa che le variabili impostate command1non andrebbero perse con la variante di sostituzione del processo.


11

< <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 lscomando che può essere letto riga per riga ed echoed nel ciclo.

<()viene utilizzato per la sostituzione del processo. Maggiori informazioni ed esempi per <()sono disponibili a questo link:

Sostituzione del processo e pipe

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.