Vraag Wat is het verschil tussen <<, <<< en <

Wat is het verschil tussen <<, <<< en < < in bash?


71
2017-09-27 07:42


oorsprong


In deze Google-tijden is het ten minste moeilijk om te zoeken naar deze op symbolen gebaseerde operatoren. Is er een zoekmachine waar u "<< <<< <<" kunt aansluiten en iets nuttigs kunt krijgen? - Daniel Griscom
@DanielGriscom Er is SymbolHound. - Dennis
@DanielGriscom Stack Exchange werd gebruikt om zoeksymbolen te ondersteunen, maar toen brak er iets en niemand heeft het ooit gerepareerd. - muru
Het is er al (en is er al bijna een jaar): Wat zijn de besturings- en omleidingsoperatoren van de shell? - Scott


antwoorden:


Hier document

<< staat bekend als here-document structuur. Je laat het programma weten wat de eindtekst is en wanneer dat scheidingsteken wordt gezien, zal het programma alle dingen lezen die je als input aan het programma hebt gegeven en er een taak op uitvoeren.

Dit is wat ik bedoel:

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

In dit voorbeeld vertellen we wc programma om op te wachten EOF string, typ vervolgens vijf woorden in en typ vervolgens in EOF om aan te geven dat we klaar zijn met het geven van input. In feite is het vergelijkbaar met hardlopen wc alleen, typ woorden in en druk vervolgens op CtrlD

In bash worden deze geïmplementeerd via tijdelijke bestanden, meestal in de vorm /tmp/sh-thd.<random string>, terwijl ze in het dashboard zijn geïmplementeerd als anonieme pijpen. Dit kan worden waargenomen via tracingsysteemoproepen met strace opdracht. Vervangen bash met sh om te zien hoe /bin/sh voert deze omleiding uit.

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

Hier tekenreeks

<<< staat bekend als here-string . In plaats van tekst in te voeren, geeft u een vooraf gemaakte reeks tekst aan een programma. Met een dergelijk programma bijvoorbeeld bc we kunnen doen bc <<< 5*4 om alleen voor dat specifieke geval uitvoer te krijgen, hoeft u BCC niet interactief uit te voeren.

Hier-strings in bash worden geïmplementeerd via tijdelijke bestanden, meestal in het formaat /tmp/sh-thd.<random string>, die later worden ontkoppeld, waardoor ze tijdelijk wat geheugenruimte innemen, maar niet worden weergegeven in de lijst met /tmp mapvermeldingen, en effectief bestaan ​​als anonieme bestanden, waarnaar nog steeds kan worden verwezen via de bestandsdescriptor door de shell zelf, en die bestandsdescriptor wordt overgenomen door de opdracht en later gedupliceerd naar bestandsdescriptor 0 (stdin) via dup2() functie. Dit kan worden waargenomen via

$ 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

En via het traceren van syscalls (uitvoer verkort voor leesbaarheid, merk op hoe het tijdelijke bestand wordt geopend als fd 3, er naar geschreven gegevens, dan wordt het opnieuw geopend met O_RDONLY markeer als fd 4 en later ontkoppeld, dan dup2() op fd 0, die wordt geërfd door cat later):

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

Advies: mogelijk omdat hier reeksen gebruik maken van tijdelijke tekstbestanden, het is de mogelijke reden waarom hier-strings altijd een nieuwe regel invoegen, aangezien tekstbestand van POSIX-definitie moet regels hebben die eindigen op een nieuwlijnteken.

Procesvervanging 

Als tldp.org legt uit,

Processubstitutie voedt de uitvoer van een proces (of processen) in   de stdin van een ander proces.

In feite is dit vergelijkbaar met piping stdout van het ene commando naar het andere, b.v. echo foobar barfoo | wc . Maar let op: in de bash manpage je zult zien dat het wordt aangeduid als <(list). Dus eigenlijk kunt u de uitvoer van meerdere (!) Opdrachten omleiden.

Notitie: technisch als je zegt < < je verwijst niet naar één ding, maar twee omleidingen met single < en procesomleiding van uitvoer van <( . . .).

Wat gebeurt er als we alleen substitutie verwerken?

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

Zoals je kunt zien, maakt de shell een tijdelijke bestandsdescriptor /dev/fd/63 waar de output naartoe gaat (wat volgens Het antwoord van Gilles, is een anonieme pijp). Dat betekent < leidt die bestandsdescriptor om als invoer in een opdracht.

Een heel eenvoudig voorbeeld zou zijn om procesvervanging van de uitvoer van twee echo-opdrachten in wc te maken:

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

Dus hier maken we shell een bestandsdescriptor maken voor alle uitvoer die gebeurt tussen de haakjes en die omleiden als invoer voor wc . Zoals verwacht, ontvangt wc die stream van twee echo-opdrachten, die zelf twee regels uitvoeren, elk met een woord, en op de juiste manier hebben we 2 woorden, 2 regels en 6 tekens plus twee nieuwe regels geteld.

Kanttekening: Procesvervanging kan worden aangeduid als een bashism (een commando of structuur die bruikbaar is in geavanceerde shells zoals bash, maar niet gespecificeerd door POSIX), maar geïmplementeerd in ksh voor het leven van bash als ksh man pagina en dit antwoord stel voor. Schelpen zoals tcsh en mksh hebben echter geen procesvervanging. Dus hoe kunnen we de uitvoer van meerdere opdrachten omleiden naar een ander commando zonder procesvervanging? Groeperen plus piping!

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

In feite is dit hetzelfde als hierboven, maar dit is anders onder de motorkap van procesvervanging, omdat we stdout maken van de hele subshell en stdin van wc  verbonden met de pijp. Aan de andere kant zorgt procesvervanging ervoor dat een opdracht een tijdelijke bestandsdescriptor leest.

Dus als we kunnen groeperen met piping, waarom hebben we procesvervanging nodig? Omdat we soms geen gebruik kunnen maken van piping. Bekijk het onderstaande voorbeeld - vergelijk de uitgangen van twee opdrachten met diff (dat heeft twee bestanden nodig, en in dit geval geven we het twee bestandsdescriptors)

diff <(ls /bin) <(ls /usr/bin)

83
2017-09-27 07:56



< < wordt gebruikt als iemand stdin krijgt van een procesvervanging. Zo'n commando kan er als volgt uitzien: cmd1 < <(cmd2). Bijvoorbeeld, wc < <(date) - John1024
Ja, procesvervanging wordt ondersteund door bash, zsh en AT & T ksh {88,93} maar niet door pdksh / mksh. - John1024
< <  is geen ding op zichzelf, in het geval van procesvervanging is het gewoon een < gevolgd door iets anders dat toevallig begint < - immibis
@muru Voor zover ik weet, <<< werd voor het eerst geïmplementeerd door de Unix-poort van Plan 9 rc shell en later overgenomen door zsh, bash en ksh93. Ik zou het dan geen bashisme noemen. - jlliagre
Een ander voorbeeld van waar piping niet kan worden gebruikt: echo 'foo' | read; echo ${REPLY} zullen niet terugkeer foo, omdat read wordt gestart in een sub-shell - piping start een sub-shell. Echter, read < <(echo 'foo'); echo ${REPLY} komt correct terug foo, omdat er geen sub-shell is. - Paddy Landau


< < is een syntaxisfout:

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

< <() is procesvervanging (<()) gecombineerd met omleiding (<):

Een gekunsteld voorbeeld:

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

Bij procesvervanging wordt het pad naar de bestandsdescriptor gebruikt als een bestandsnaam. Als u een bestandsnaam niet direct wilt (of kunt) gebruiken, combineert u procesvervanging met omleiding.

Voor de duidelijkheid, er is geen < < operator.


22
2017-09-27 08:05



ik begrijp aan je antwoord dat <<() nuttiger dan <() toch? - solfish
@solfish <() geeft een bestandsnaam-achtige ding, dus het is meer in het algemeen nuttig - < <() is de stdin aan het vervangen als dat misschien niet nodig is. In wc, de laatste is nuttiger. Het is misschien elders minder handig - muru


< < is een syntaxisfout, bedoel je waarschijnlijk command1 < <( command2 ) Dit is een eenvoudige input-omleiding gevolgd door een procesvervanging en is zeer vergelijkbaar maar niet equivalent aan:

command2 | command1

Het verschil, ervan uitgaande dat je rent bash is command1 wordt uitgevoerd in een subshell in het tweede geval terwijl het wordt uitgevoerd in de huidige shell in de eerste. Dat betekent dat er variabelen zijn ingesteld command1 zou niet verloren gaan met de procesvervangingsvariant.


10
2017-09-27 08:09





< < geeft een syntaxisfout. Correct gebruik is als volgt:

Uitleggen met behulp van voorbeelden:

Voorbeeld voor < <():

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

In het bovenstaande voorbeeld komt de invoer voor de while-lus uit de ls commando dat regel voor regel en kan worden gelezen echoin de lus.

<() wordt gebruikt voor procesvervanging. Meer informatie en een voorbeeld voor <()is te vinden op deze link:

Procesvervanging en pijp


8
2017-09-27 08:06