Jeg undrede mig over, hvordan jeg skulle forstå følgende:
Piping af kommandos stdout i en andens stdin er en stærk teknik. Men hvad hvis du har brug for at røre stdout af flere kommandoer? Det er her, procesudskiftning kommer ind.
Med andre ord, kan procesudskiftning gøre hvad rør kan gøre?
Hvad kan procesudskiftning gøre, men rør ikke?
Svar
En god måde at grok forskellen mellem dem er at eksperimentere lidt på kommandolinjen. På trods af den visuelle lighed ved brug af <
karakteren, gør det noget meget andet end en omdirigering eller et rør.
Lad os bruge date
kommando til test.
$ date | cat Thu Jul 21 12:39:18 EEST 2011
Dette er et meningsløst eksempel, men det viser, at cat
accepterede output fra date
på STDIN og spyttede det ud igen. De samme resultater kan opnås ved procesudskiftning:
$ cat <(date) Thu Jul 21 12:40:53 EEST 2011
Men hvad der lige skete bag kulisserne, var anderledes. I stedet for at få en STDIN-strøm blev cat
faktisk sendt navnet på en fil, som den havde brug for for at åbne og læs. Du kan se dette trin ved at bruge echo
i stedet for cat
.
$ echo <(date) /proc/self/fd/11
Når katten modtog filnavnet, læste den filens indhold for os. På den anden side viste ekko os bare filens navn, som den blev sendt. Denne forskel bliver mere tydelig, hvis du tilføjer flere erstatninger:
$ 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
Det er det muligt at kombinere proceserstatning (som genererer en fil) og input omdirigering (som forbinder en fil til STDIN):
$ cat < <(date) Thu Jul 21 12:46:22 EEST 2011
Det ser stort set det samme ud, men denne gang blev katten sendt STDIN-strøm i stedet for et filnavn. Du kan se dette ved at prøve det med ekko:
$ echo < <(date) <blank>
Da ekko ikke læser STDIN og intet argument blev bestået, vi får intet.
Rør og input omdirigeringer skubber indhold til STDIN-strømmen. Processubstitution kører kommandoerne, gemmer deres output i en særlig midlertidig fil og sender derefter filnavnet i stedet for kommandoen. Uanset hvilken kommando du bruger, behandles det som et filnavn. Bemærk, at den oprettede fil ikke er en almindelig fil, men et navngivet rør, der automatisk fjernes, når det ikke længere er nødvendigt.
Kommentarer
Svar
Her er tre ting, du kan gøre med proceserstatning, der ellers er umulige.
Flere procesindgange
diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls)
Der det er simpelthen ingen måde at gøre dette på med rør.
Bevaring af STDIN
Sig, at du har følgende:
curl -o - http://example.com/script.sh #/bin/bash read LINE echo "You said ${LINE}!"
Og du vil køre det direkte. Følgende mislykkes elendigt. Bash bruger allerede STDIN til at læse scriptet, så andet input er umuligt.
curl -o - http://example.com/script.sh | bash
Men denne måde fungerer perfekt.
bash <(curl -o - http://example.com/script.sh)
Udskiftning af proceserstatning
Bemærk også, at proceserstatning fungerer også den anden vej. Så du kan gøre noget som dette:
(ls /proc/*/exe >/dev/null) 2> >(sed -n \ "/Permission denied/ s/.*\(\/proc.*\):.*/\1/p" > denied.txt )
Det er lidt af et indviklet eksempel, men det sender stdout til /dev/null
, mens du rør stderr til et sed-script for at udtrække navnene på de filer, som en ” Tilladelse nægtet ” fejl blev vist, og sender derefter disse resultater til en fil.
Bemærk, at den første kommando og omdirigering stdout er i parentes ( subshell ), så kun resultatet af DEN-kommando sendes til /dev/null
, og det roder ikke med resten af linjen.
Kommentarer
- Det ‘ er værd at bemærke, at i
diff
eksempel vil du måske være interesseret i sagen, hvorcd
muligvis mislykkes:diff <(cd /foo/bar/ && ls) <(cd /foo/baz && ls)
. - ” mens rørledning stderr “: er ikke ‘ t påpege, at dette er ikke piping, men gennemgår en fifo-fil?
- @Gauthier no; kommandoen bliver ikke erstattet med en fifo, men med en henvisning til filbeskrivelsen. Så ” echo < (echo) ” skal give noget som ” / dev / fd / 63 “, som er en enhed med specialtegn, der læser eller skriver fra FD-nummer 63.
Svar
Jeg skulle antage, at du taler om bash
eller en anden avanceret shell, fordi posix shell har ikke proceserstatning .
bash
manuelle siderapporter:
Processubstitution
Processerstatning understøttes på systemer, der understøtter navngivne rør (FIFOer) eller / dev / fd-metoden til navngivning af åbne filer. Det har form af < (liste) eller> (liste). Processlisten køres med dens input eller output tilsluttet en FIFO eller en fil i / dev / fd. Navnet på denne fil sendes som et argument til den aktuelle kommando som resultat af udvidelsen. Hvis formularen> (liste) anvendes, vil skrivning til filen give input til listen. Hvis formularen < (liste) bruges, skal filen, der sendes som et argument, læses for at få output fra listen.Når det er tilgængeligt, skal procesudskiftning udføres samtidigt med parameter- og variabeludvidelse, kommandosubstitution og aritmetisk udvidelse.
Med andre ord og fra et praktisk synspunkt kan du bruge et udtryk som det følgende
<(commands)
som et filnavn til andre kommandoer, der kræver en fil som parameter. Eller du kan bruge omdirigering til en sådan fil:
while read line; do something; done < <(commands)
Når du vender tilbage til dit spørgsmål, ser det ud til, at procesudskiftning og rør ikke har meget til fælles.
Hvis du vil røre i rækkefølge output af flere kommandoer, kan du bruge en af følgende former:
(command1; command2) | command3 { command1; command2; } | command3
men du kan også bruge omdirigering ved proceserstatning
command3 < <(command1; command2)
endelig, hvis command3
accepterer en filparameter (i stedet for stdin )
command3 <(command1; command2)
Kommentarer
- så < ( ) og < < () har samme effekt, ikke?
- @solfish: ikke exacllty: branden kan bruges overalt, hvor et filnavn forventes, det andet er en inputomdirigering til det filnavn
Svar
Hvis en kommandoen tager en liste over filer som argumenter og behandler disse filer som input (eller output, men ikke almindeligt), kan hver af disse filer være et navngivet rør eller / dev / fd pseudo-fil, der tilvejebringes gennemsigtigt ved processubstitution:
$ sort -m <(command1) <(command2) <(command3)
Dette vil “pibe” output af de tre kommandoer, der skal sorteres, da sortering kan tage en liste med inputfiler på kommandolinjen.
Kommentarer
- IIRC < (kommando) syntaks er kun en bash-funktion.
- @Philomath: Det ‘ er i ZSH også.
- Nå, ZSH har alt … (eller prøver i det mindste).
- @Philomath: Hvordan implementeres procesudskiftning i andre skaller?
- @Philomath
<()
var, ligesom mange avancerede shell-funktioner, oprindeligt en ksh-funktion og blev vedtaget af bash og zsh.psub
er specifikt en fiskefunktion, intet at gøre med POSIX.
Svar
Det skal bemærkes, at procesudskiftning ikke er begrænset til formen <(command)
, som bruger output fra command
fil. Det kan være i formen >(command)
, som også føder en fil som input til command
. Dette er også nævnt i citatet af bash manual i @enzotibs svar.
For date | cat
eksemplet ovenfor er en kommando, der bruger proceserstatningen af form >(command)
for at opnå den samme effekt ville være,
date > >(cat)
Bemærk at >
før >(cat)
er nødvendigt. Dette kan igen illustreres tydeligt med echo
som i @Calebs svar.
$ echo >(cat) /dev/fd/63
Så uden det ekstra >
ville date >(cat)
være samme som date /dev/fd/63
som udskriver en besked til stderr.
Antag at du har et program, der kun tager filnavne som parametre og ikke behandler stdin
eller stdout
.Jeg vil bruge det forenklede script psub.sh
til at illustrere dette. Indholdet af psub.sh
er
#!/bin/bash [ -e "$1" -a -e "$2" ] && awk "{print $1}" "$1" > "$2"
Grundlæggende tester det, at begge dets argumenter er filer (ikke nødvendigvis regelmæssige filer), og hvis dette er tilfældet, skal du skrive det første felt i hver linje af "$1"
til "$2"
ved hjælp af awk. Derefter er en kommando, der kombinerer alt det, der er nævnt indtil videre,
./psub.sh <(printf "a a\nc c\nb b") >(sort)
Dette udskriver
a b c
og svarer til
printf "a a\nc c\nb b" | awk "{print $1}" | sort
men følgende fungerer ikke, og vi er nødt til at bruge proceserstatning her,
printf "a a\nc c\nb b" | ./psub.sh | sort
eller dets tilsvarende form
printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort
Hvis ./psub.sh
også læser stdin
udover det, der er nævnt ovenfor, eksisterer en sådan ækvivalent form ikke, og i så fald er der intet vi kan bruge i stedet for proceserstatningen (selvfølgelig kan du også bruge en navngivet rør- eller temp-fil, men det er en anden historie).
[[ -p <(date) ]] && echo true
. Dette producerertrue
når jeg kører det med bash 4.4 eller 3.2.