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
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 decd
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).
[[ -p <(date) ]] && echo true
. Dit leverttrue
op als ik het uitvoer met bash 4.4 of 3.2.