Proceserstatning og rør

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

  • Hvis jeg forstået korrekt, tldp.org/LDP/abs/html/process-sub.html#FTN.AEN18244 siger, at procesudskiftning opretter midlertidige filer, ikke navngivne rør. Så vidt jeg ved, skal du ikke oprette midlertidige filer. Skrivning til røret indebærer aldrig skrivning til disk: stackoverflow.com/a/6977599/788700
  • Jeg ved, at dette svar er legitimt ‘ fordi det bruger ordet grok : D
  • @Adobe kan du bekræfte, om den midlertidige filprocesudskiftning producerer er et navngivet rør med: [[ -p <(date) ]] && echo true. Dette producerer true når jeg kører det med bash 4.4 eller 3.2.

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, hvor cd 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).

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *