Procesvervanging en pipe

Ik vroeg me af hoe ik het volgende moest begrijpen:

De stdout van een commando in de stdin van een ander omzetten is een krachtige techniek. Maar wat als u de standaard van meerdere opdrachten moet doorspoelen? Dit is waar procesvervanging om de hoek komt kijken.

Met andere woorden, kan procesvervanging doen wat pipe kan doen?

Wat kan procesvervanging doen, maar pipe niet?

Antwoord

Een goede manier om de Het verschil tussen hen is om een beetje te experimenteren op de opdrachtregel. Ondanks de visuele gelijkenis in het gebruik van het < -teken, doet het iets heel anders dan een redirect of pipe.

Laten we de date commando voor testen.

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

Dit is een zinloos voorbeeld, maar het laat zien dat cat accepteerde de uitvoer van date op STDIN en spuugde het weer uit. Dezelfde resultaten kunnen worden bereikt door procesvervanging:

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

Wat er net achter de schermen gebeurde was echter anders. In plaats van een STDIN-stream te krijgen, kreeg cat eigenlijk de naam van een bestand dat het nodig had om te openen en lees. Je kunt deze stap zien door echo te gebruiken in plaats van cat.

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

Toen cat de bestandsnaam ontving, las het de inhoud van het bestand voor ons. Aan de andere kant liet echo ons gewoon de bestandsnaam zien dat het werd doorgegeven. Dit verschil wordt duidelijker als je meer vervangingen toevoegt:

$ 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 

is mogelijk om processubstitutie (die een bestand genereert) en invoeromleiding (die een bestand met STDIN verbindt) te combineren:

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

Het ziet er ongeveer hetzelfde uit, maar deze keer werd kat STDIN-stream doorgegeven in plaats van een bestandsnaam. Je kunt dit zien door het te proberen met echo:

$ echo < <(date) <blank> 

Aangezien echo STDIN niet leest en er is geen argument doorgegeven, we krijgen niets.

Pijpen en invoeromleidingen schuiven inhoud naar de STDIN-stream. Procesvervanging voert de opdrachten uit, slaat hun uitvoer op in een speciaal tijdelijk bestand en geeft vervolgens die bestandsnaam door in plaats van de opdracht. Welk commando u ook gebruikt, het behandelt het als een bestandsnaam. Merk op dat het aangemaakte bestand geen gewoon bestand is, maar een named pipe die automatisch wordt verwijderd zodra het niet langer nodig is.

Opmerkingen

  • Als ik goed begrepen, tldp.org/LDP/abs/html/process-sub.html#FTN.AEN18244 zegt dat procesvervanging tijdelijke bestanden creëert, niet genaamd pipes. Voor zover ik weet, maak je geen tijdelijke bestanden aan. Schrijven naar de pipe houdt nooit in dat naar schijf wordt geschreven: stackoverflow.com/a/6977599/788700
  • Ik weet dat dit antwoord legitiem is ‘ omdat het het woord grok : D
  • @Adobe gebruikt, kun je bevestigen of de vervanging van het tijdelijke bestandsproces een named pipe is met: [[ -p <(date) ]] && echo true. Dit levert true op als ik het uitvoer met bash 4.4 of 3.2.

Answer

Hier zijn drie dingen die u kunt doen met procesvervanging die anders onmogelijk zijn.

Meerdere procesinvoer

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

Daar Dit is eenvoudigweg niet te doen met pijpen.

STDIN behouden

Stel dat je het volgende hebt:

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

En u wilt het rechtstreeks uitvoeren. Het volgende mislukt jammerlijk. Bash gebruikt STDIN al om het script te lezen, dus andere invoer is onmogelijk.

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

Maar deze manier werkt perfect.

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

Uitgaande procesvervanging

Merk ook op dat procesvervanging ook de andere kant op werkt. Dus je kunt zoiets als dit doen:

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

Dat “is een beetje een ingewikkeld voorbeeld, maar het stuurt stdout naar /dev/null, terwijl stderr wordt doorgesluisd naar een sed-script om de namen te extraheren van de bestanden waarvoor een ” Toestemming geweigerd ” fout werd weergegeven, en verzendt dan DEZE resultaten naar een bestand.

Merk op dat het eerste commando en de stdout omleiding tussen haakjes staan ( subshell ) zodat alleen het resultaat van DAT commando naar /dev/null wordt gestuurd en het niet “rommelt met de rest van de regel.

Reacties

  • Het ‘ is het vermelden waard dat in de diff voorbeeld dat u wellicht zou willen geven over het geval waarin de cd zou kunnen falen: diff <(cd /foo/bar/ && ls) <(cd /foo/baz && ls).
  • ” terwijl stderr “: isn ‘ t de wijs erop dat dit geen piping is, maar door een fifo-bestand gaat?
  • @Gauthier nee; het commando wordt niet vervangen door een fifo maar met een verwijzing naar de bestandsdescriptor. Dus ” echo < (echo) ” zou iets moeten opleveren als ” / dev / fd / 63 “, een apparaat met een speciaal teken dat leest of schrijft van FD-nummer 63.

Answer

Ik neem aan dat je het hebt over bash of een andere geavanceerde shell, omdat de posix shell heeft geen procesvervanging .

bash handmatige paginarapporten:

Procesvervanging
Procesvervanging wordt ondersteund op systemen die named pipes (FIFOs) of de / dev / fd-methode voor het benoemen van open bestanden ondersteunen. Het heeft de vorm van < (lijst) of> (lijst). De proceslijst wordt uitgevoerd met de invoer of uitvoer verbonden met een FIFO of een bestand in / dev / fd. De naam van dit bestand wordt als een argument aan de huidige opdracht doorgegeven als resultaat van de uitbreiding. Als het> (lijst) formulier wordt gebruikt, zal schrijven naar het bestand input voor de lijst opleveren. Als het < (lijst) formulier wordt gebruikt, moet het bestand dat als argument is doorgegeven worden gelezen om de uitvoer van lijst te verkrijgen.

Indien beschikbaar, verwerk substitutie wordt gelijktijdig uitgevoerd met uitbreiding van parameters en variabelen, vervanging van opdrachten en rekenkundige uitbreiding.

Met andere woorden, en vanuit praktisch oogpunt, kunt u gebruiken een uitdrukking zoals de volgende

<(commands) 

als een bestandsnaam voor andere commandos die een bestand als parameter vereisen. Of je kunt redirection gebruiken voor zon bestand:

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

Terugkomend op je vraag, het lijkt mij dat procesvervanging en pipes niet veel gemeen hebben.

Als u de uitvoer van meerdere opdrachten achter elkaar wilt doorsluizen, kunt u een van de volgende formulieren gebruiken:

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

maar u kan ook omleiding gebruiken bij procesvervanging

command3 < <(command1; command2) 

tenslotte, als command3 een bestandsparameter accepteert (in plaats van stdin )

command3 <(command1; command2) 

Reacties

  • dus < ( ) en < < () heeft hetzelfde effect, toch?
  • @solfish: not exacllty: the firse can worden gebruikt waar een bestandsnaam wordt verwacht, de tweede is een invoeromleiding voor die bestandsnaam

Antwoord

Als een commando neemt een lijst met bestanden als argumenten en verwerkt die bestanden als invoer (of output, maar niet vaak), kan elk van die bestanden een named pipe of / dev / fd pseudo-bestand zijn, transparant geleverd door procesvervanging:

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

Dit zal de uitvoer van de drie commandos “pijpen” om te sorteren, aangezien sorteren een lijst van invoerbestanden op de commandoregel kan aannemen.

Opmerkingen

  • IIRC de < (command) syntaxis is een bash-only feature.
  • @Philomath: Het ‘ s in ZSH ook.
  • Nou, ZSH heeft alles … (of probeert het tenminste).
  • @Philomath: Hoe wordt procesvervanging geïmplementeerd in andere shells?
  • @Philomath <() was, zoals veel geavanceerde shell-functies, oorspronkelijk een ksh-functie en werd overgenomen door bash en zsh. psub is specifiek een visfunctie, heeft niets te maken met POSIX.

Antwoord

Opgemerkt moet worden dat procesvervanging niet beperkt is tot de vorm <(command), die de uitvoer van command gebruikt als een het dossier. Het kan de vorm >(command) hebben die een bestand als invoer ook naar command voert. Dit wordt ook vermeld in het citaat van de bash-handleiding in het antwoord van @enzotib.

Voor het date | cat voorbeeld hierboven, een commando dat de procesvervanging van de form >(command) om hetzelfde effect te bereiken zou zijn,

date > >(cat) 

Merk op dat de > voordat >(cat) nodig is. Dit kan weer duidelijk worden geïllustreerd door echo zoals in het antwoord van @Caleb.

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

Dus zonder de extra >, date >(cat) zou de hetzelfde als date /dev/fd/63 die een bericht naar stderr zal afdrukken.

Stel dat je een programma hebt dat alleen bestandsnamen als parameters aanneemt en of stdout.Ik zal het versimpelde script psub.sh gebruiken om dit te illustreren. De inhoud van psub.sh is

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

In wezen test het of beide argumenten bestanden zijn (niet noodzakelijkerwijs reguliere bestanden) en als dit het geval is, schrijf dan het eerste veld van elke regel van "$1" naar "$2" met awk. Dan is een commando dat alles combineert wat tot nu toe is genoemd:

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

Dit zal afdrukken

a b c 

en is gelijk aan

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

maar het volgende zal niet werken, en we moeten hier processubstitutie gebruiken,

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

of zijn gelijkwaardige vorm

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

Als ./psub.sh ook leest stdin naast wat hierboven is vermeld, dan bestaat zon gelijkwaardige vorm niet, en in dat geval is er niets dat we kunnen gebruiken in plaats van de procesvervanging (je kunt natuurlijk ook een genaamd pipe of temp-bestand, maar dat is een ander verhaal).

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *