Jaka jest różnica między <<, <<< i <<w bash?

102

Jaka jest różnica między <<, <<<a < <w bash?

Searene
źródło
20
Przynajmniej w czasach zorientowanych na Google trudno jest wyszukać operatorów opartych na symbolach. Czy istnieje wyszukiwarka, w której można podłączyć „<< <<< <<” i uzyskać coś przydatnego?
Daniel Griscom
11
@DanielGriscom There SymbolHound .
Dennis
1
@DanielGriscom Stack Exchange służył do obsługi wyszukiwania symboli, ale potem coś się zepsuło i nikt tego nie naprawił.
muru
Jest już dostępny (i jest już od prawie roku): Jakie są operatory kontroli i przekierowania powłoki?
Scott,

Odpowiedzi:

115

Tutaj dokument

<<jest znany jako here-documentstruktura. Dajesz programowi znać, co będzie końcowym tekstem, i za każdym razem, gdy zobaczysz ten ogranicznik, program odczyta wszystkie dane, które przekazałeś programowi jako dane wejściowe, i wykona na nim zadanie.

Oto co mam na myśli:

$ wc << EOF
> one two three
> four five
> EOF
 2  5 24

W tym przykładzie mówimy wcprogramowi, aby czekał na EOFciąg znaków, a następnie wpisz pięć słów, a następnie wpisz, EOFaby zasygnalizować, że wprowadzono dane wejściowe. W rzeczywistości jest podobny do biegania wcsamemu, wpisywania słów, a następnie naciskaniaCtrlD

W bash są one implementowane za pomocą plików tymczasowych, zwykle w formie /tmp/sh-thd.<random string>, natomiast w dash są implementowane jako anonimowe potoki. Można to zaobserwować poprzez śledzenie wywołań systemowych za pomocą stracepolecenia. Wymień bashsię sh, aby zobaczyć, jak /bin/shwykonuje to przekierowanie.

$ strace -e open,dup2,pipe,write -f bash -c 'cat <<EOF
> test
> EOF'

Tutaj ciąg

<<<jest znany jako here-string. Zamiast wpisywać tekst, podajesz programowi gotowy ciąg tekstu. Na przykład przy takim programie, bcjaki możemy zrobić, bc <<< 5*4aby uzyskać dane wyjściowe dla tego konkretnego przypadku, nie trzeba interakcyjnie uruchamiać bc.

Tutaj ciągi w bash są implementowane za pomocą plików tymczasowych, zwykle w formacie /tmp/sh-thd.<random string>, które później są rozłączane, dzięki czemu zajmują tymczasowo pewną przestrzeń pamięci, ale nie pojawiają się na liście /tmppozycji katalogu i skutecznie istnieją jako pliki anonimowe, które mogą nadal odwołuje się do deskryptora pliku przez samą powłokę, a ten deskryptor pliku jest dziedziczony przez polecenie, a następnie powielany na deskryptorze pliku 0 (stdin) przez dup2()funkcję. Można to zaobserwować poprzez

$ 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

I poprzez śledzenie wywołań systemowych (dane wyjściowe skrócone dla czytelności; zwróć uwagę, jak plik tymczasowy jest otwierany jako fd 3, dane do niego zapisywane, następnie jest ponownie otwierany z O_RDONLYflagą jako fd 4, a następnie odłączony, a następnie dup2()na fd 0, który jest dziedziczony catpóźniej) ):

$ 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 +++

Opinia: potencjalnie ponieważ tutaj łańcuchy wykorzystują tymczasowe pliki tekstowe, jest to możliwy powód, dla którego tutaj łańcuchy zawsze wstawiają końcowy znak nowej linii, ponieważ plik tekstowy według definicji POSIX musi mieć linie kończące się znakiem nowego wiersza.

Zastąpienie procesu

Jak wyjaśnia tldp.org ,

Podstawienie procesu podaje dane wyjściowe procesu (lub procesów) do standardowego wejścia innego procesu.

W efekcie jest to podobne do przesyłania potokiem jednego polecenia do drugiego, np echo foobar barfoo | wc. Ale zauważ: na stronie bash zobaczysz, że jest oznaczony jako <(list). Zasadniczo możesz przekierować dane wyjściowe wielu (!) Poleceń.

Uwaga: technicznie rzecz biorąc, gdy mówisz, < <że nie masz na myśli jednej rzeczy, ale dwa przekierowania z pojedynczym <i procesowym przekierowaniem wyjścia z <( . . .).

Co się stanie, jeśli dokonamy tylko zamiany?

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

Jak widać, powłoka tworzy tymczasowy deskryptor pliku, do /dev/fd/63którego trafia dane wyjściowe (co według odpowiedzi Gillesa jest anonimową potokiem). Oznacza to, <że przekierowuje ten deskryptor pliku jako dane wejściowe do polecenia.

Tak bardzo prostym przykładem może być zamiana procesu podstawiania danych wyjściowych z dwóch poleceń echa na wc:

$ wc < <(echo bar;echo foo)
      2       2       8

Więc tutaj zmuszamy powłokę do utworzenia deskryptora pliku dla wszystkich danych wyjściowych w nawiasach i przekierowania, które jako dane wejściowe do wc. Jak oczekiwano, wc odbiera ten strumień z dwóch poleceń echa, które same z siebie wyprowadziłyby dwie linie, z których każda zawiera słowo, i odpowiednio mamy 2 słowa, 2 linie i 6 znaków plus dwie nowe linie.

Uwaga dodatkowa: Podstawienie procesu może być określane jako bashism (polecenie lub struktura użyteczne w zaawansowanych powłokach takich jak bash, ale nie określone przez POSIX), ale zostało zaimplementowane kshprzed istnieniem basha jako strona podręcznika użytkownika ksh i ta odpowiedź sugeruje. Powłoki jak tcshi mkshjednak nie mają substytucji procesu. Jak więc moglibyśmy przekierowywać wyjście wielu poleceń do innego polecenia bez zastępowania procesu? Grupowanie plus orurowanie!

$ (echo foo;echo bar) | wc
      2       2       8

W rzeczywistości jest to to samo co w powyższym przykładzie, jednak różni się to pod maską od zastępowania procesu, ponieważ wykonujemy stdout całej podpowłoki i stdin wc połączony z rurą . Z drugiej strony podstawienie procesu powoduje, że polecenie czyta tymczasowy deskryptor pliku.

Jeśli więc możemy grupować za pomocą pipingu, dlaczego potrzebujemy podstawiania procesów? Ponieważ czasami nie możemy używać rur. Rozważ poniższy przykład - porównanie wyników dwóch poleceń z diff(które wymagają dwóch plików, w tym przypadku podajemy dwa deskryptory plików)

diff <(ls /bin) <(ls /usr/bin)
Sergiy Kolodyazhnyy
źródło
7
< <jest używany, gdy otrzymuje się standardowe wejście z podstawienia procesu . Takie polecenie może wyglądać tak: cmd1 < <(cmd2). Na przykładwc < <(date)
John1024
4
Tak, podstawianie procesów jest obsługiwane przez bash, zsh i AT&T ksh {88,93}, ale nie przez pdksh / mksh.
John1024
2
< < nie jest rzeczą samą w sobie, w przypadku substytucji procesu <następuje po niej coś innego, co zaczyna się od<
immibis
1
@muru O ile wiem, <<<najpierw został zaimplementowany przez port uniksowy powłoki Plan 9 rc, a następnie przyjęty przez zsh, bash i ksh93. Nie nazwałbym tego bazmem.
jlliagre
3
Innym przykładem, w którym nie można stosować rurowe: echo 'foo' | read; echo ${REPLY}będzie nie powróci foo, ponieważ readjest rozpoczynane w podpowłoce - orurowanie rozpoczyna podpowłoce. Jednak read < <(echo 'foo'); echo ${REPLY}poprawnie zwraca foo, ponieważ nie ma podpowłoki.
Paddy Landau
26

< < jest błędem składni:

$ cat < <
bash: syntax error near unexpected token `<'

< <()to proces podstawiania ( <()) w połączeniu z przekierowaniem ( <):

Przemyślany przykład:

$ wc -l < <(grep ntfs /etc/fstab)
4
$ wc -l <(grep ntfs /etc/fstab)
4 /dev/fd/63

W przypadku podstawienia procesu ścieżka do deskryptora pliku jest używana jak nazwa pliku. W przypadku, gdy nie chcesz (lub nie możesz) bezpośrednio użyć nazwy pliku, łączysz podstawienie procesu z przekierowaniem.

Żeby było jasne, nie ma < <operatora.

muru
źródło
otrzymuję odpowiedź, że <<() jest bardziej przydatne niż <() prawda?
solfish
1
@solfish <()daje nazwę podobną do nazwy pliku, więc jest bardziej użyteczna - < <()zastępuje stdin tam, gdzie może nie być konieczne. W wctym drugim przypadku okazuje się bardziej przydatne. Może być mniej przydatny gdzie indziej
muru
12

< <to błąd składniowy, prawdopodobnie masz na myśli command1 < <( command2 )proste przekierowanie wejścia, po którym następuje podstawienie procesu i jest bardzo podobne, ale nie równoważne z:

command2 | command1

Różnica przy założeniu, że działasz, bashjest command1uruchamiana w podpowłoce w drugim przypadku, podczas gdy jest uruchamiana w bieżącej powłoce w pierwszym. Oznacza to, że ustawione zmienne command1nie zostaną utracone w wariancie substytucji procesu.

jlliagre
źródło
11

< <da błąd składniowy. Właściwe użycie jest następujące:

Wyjaśnienie za pomocą przykładów:

Przykład dla < <():

while read line;do
   echo $line
done< <(ls)

W powyższym przykładzie dane wejściowe do pętli while będą pochodzić z lspolecenia, które można odczytać wiersz po wierszu i echoedytować w pętli.

<()służy do podstawienia procesu. Więcej informacji i przykład <()można znaleźć pod tym linkiem:

Zastąpienie procesu i rura

podejrzeć
źródło