Jeg lurte på hvordan jeg skulle forstå følgende:
Å peke stdout av en kommando i stdin til en annen er en kraftig teknikk. Men hva om du trenger å peke ut stdout av flere kommandoer? Det er her prosessubstitusjon kommer inn.
Kan prosessubstitusjon med andre ord gjøre hva rør kan gjøre?
Hva kan prosessubstitusjon gjøre, men rør kan ikke?
Svar
En god måte å grok forskjellen mellom dem er å gjøre litt eksperimentering på kommandolinjen. Til tross for den visuelle likheten i bruk av <
-tegnet, gjør den noe helt annet enn en omdirigering eller et rør.
La oss bruke date
kommando for testing.
$ date | cat Thu Jul 21 12:39:18 EEST 2011
Dette er et meningsløst eksempel, men det viser at cat
aksepterte utdata fra date
på STDIN og spyttet den ut igjen. De samme resultatene kan oppnås ved prosessubstitusjon:
$ cat <(date) Thu Jul 21 12:40:53 EEST 2011
Men det som nettopp skjedde bak kulissene var annerledes. I stedet for å få en STDIN-strøm fikk cat
faktisk navnet på en fil som den trengte for å åpne og les. Du kan se dette trinnet ved å bruke echo
i stedet for cat
.
$ echo <(date) /proc/self/fd/11
Når katten mottok filnavnet, leste den filens innhold for oss. På den annen side viste ekko oss bare filens navn at den ble sendt. Denne forskjellen blir tydeligere hvis du legger til 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
It er mulig å kombinere prosessubstitusjon (som genererer en fil) og inndiretning (som kobler en fil til STDIN):
$ cat < <(date) Thu Jul 21 12:46:22 EEST 2011
Det ser ganske likt ut, men denne gangen ble katten sendt STDIN-strøm i stedet for et filnavn. Du kan se dette ved å prøve det med ekko:
$ echo < <(date) <blank>
Siden ekko ikke leser STDIN og ingen argumenter ble sendt, vi får ingenting.
Rør og viderekoblinger skyver innhold til STDIN-strømmen. Prosessubstitusjon kjører kommandoene, lagrer utdataene i en spesiell midlertidig fil og sender deretter filnavnet i stedet for kommandoen. Uansett hvilken kommando du bruker, behandles den som et filnavn. Merk at filen som er opprettet ikke er en vanlig fil, men et navngitt rør som fjernes automatisk når det ikke lenger er behov for det.
Kommentarer
Svar
Her er tre ting du kan gjøre med prosessubstitusjon som ellers er umulig.
Flere prosessinnganger
diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls)
Der Det er ganske enkelt ingen måte å gjøre dette med rør.
Bevare STDIN
Si at du har følgende:
curl -o - http://example.com/script.sh #/bin/bash read LINE echo "You said ${LINE}!"
Og du vil kjøre det direkte. Følgende mislykkes stort. Bash bruker allerede STDIN for å lese skriptet, så andre inndata er umulige.
curl -o - http://example.com/script.sh | bash
Men denne måten fungerer perfekt.
bash <(curl -o - http://example.com/script.sh)
Substitusjon for utgående prosess
Vær også oppmerksom på at prosessubstitusjon fungerer også den andre veien. Så du kan gjøre noe sånt som dette:
(ls /proc/*/exe >/dev/null) 2> >(sed -n \ "/Permission denied/ s/.*\(\/proc.*\):.*/\1/p" > denied.txt )
Det er litt av et innviklet eksempel, men det sender stdout til /dev/null
, mens du rør stderr til et sed-skript for å trekke ut navnene på filene som » Tillatelse nektet » feil ble vist, og sender deretter DENNE resultatene til en fil.
Merk at den første kommandoen og stdout omdirigering er i parentes ( subshell ) slik at bare resultatet av DEN kommandoen blir sendt til /dev/null
og den ikke roter med resten av linjen.
Kommentarer
- Det ‘ er verdt å merke seg at i
diff
eksempel vil du kanskje bry deg om saken dercd
kan mislykkes:diff <(cd /foo/bar/ && ls) <(cd /foo/baz && ls)
. - » mens rørledning «: er ikke ‘ t påpeke at dette er ikke piping, men går gjennom en fifo-fil?
- @Gauthier no; kommandoen blir ikke erstattet med en fifo, men med en referanse til filbeskrivelsen. Så » ekko < (echo) » skal gi noe sånt som » / dev / fd / 63 «, som er et spesialtegn som leser eller skriver fra FD-nummer 63.
Svar
Jeg antar at du snakker om bash
eller et annet avansert skall, fordi posix skallet har ikke prosessubstitusjon .
bash
manuelle siderapporter:
Prosessubstitusjon
Prosessubstitusjon støttes på systemer som støtter navngitte rør (FIFOs) eller / dev / fd-metoden for å navngi åpne filer. Det tar form av < (liste) eller> (liste). Prosesslisten kjøres med inngang eller utgang koblet til en FIFO eller en fil i / dev / fd. Navnet på denne filen sendes som et argument til den gjeldende kommandoen som et resultat av utvidelsen. Hvis skjemaet> (liste) brukes, vil skriving til filen gi innspill til listen. Hvis < (liste) -skjemaet brukes, skal filen som sendes som et argument leses for å få utdata fra listen.Når det er tilgjengelig, prosesserstatning utføres samtidig med parameter- og variabelutvidelse, kommandosubstitusjon og aritmetisk utvidelse.
Med andre ord, og fra et praktisk synspunkt kan du bruke et uttrykk som følgende
<(commands)
som filnavn for andre kommandoer som krever en fil som parameter. Eller du kan bruke omdirigering for en slik fil:
while read line; do something; done < <(commands)
Når du snur tilbake til spørsmålet ditt, ser det ut til at prosessubstitusjon og rør ikke har mye til felles.
Hvis du vil rense utdataene fra flere kommandoer i rekkefølge, kan du bruke en av følgende former:
(command1; command2) | command3 { command1; command2; } | command3
men du kan også bruke omdirigering ved prosessubstitusjon
command3 < <(command1; command2)
til slutt, hvis command3
godtar en filparameter (i erstatning for stdin )
command3 <(command1; command2)
Kommentarer
- så < ( ) og < < () gir samme effekt, ikke sant?
- @solfish: ikke exacllty: brannen kan brukes uansett hvor det forventes et filnavn, det andre er en inndirigering for det filnavnet
Svar
Hvis en kommandoen tar en liste over filer som argumenter og behandler filene som input (eller output, men ikke ofte), kan hver av disse filene være et navngitt rør eller / dev / fd pseudofil som er gitt transparent av prosessubstitusjon:
$ sort -m <(command1) <(command2) <(command3)
Dette vil «pipe» utgangen av de tre kommandoene som skal sorteres, ettersom sort kan ta en liste over inndatafiler på kommandolinjen.
Kommentarer
- IIRC < (kommando) syntaksen er bare en bash-funksjon.
- @Philomath: Den ‘ er i ZSH også.
- Vel, ZSH har alt … (eller prøver i det minste).
- @Philomath: Hvordan implementeres prosesserstatning i andre skall?
- @Philomath
<()
, som mange avanserte skallfunksjoner, var opprinnelig en ksh-funksjon og ble adoptert av bash og zsh.psub
er spesifikt en fiskefunksjon, ingenting med POSIX å gjøre.
Svar
Det bør bemerkes at substitusjon av prosesser ikke er begrenset til skjemaet <(command)
, som bruker utdata fra command
fil. Det kan være i formen >(command)
som også mater en fil som inngang til command
. Dette er også nevnt i sitatet til bash manual i @enzotibs svar.
For date | cat
eksempelet ovenfor, en kommando som bruker prosesserstatning av skjema >(command)
for å oppnå samme effekt ville være,
date > >(cat)
Merk at >
før >(cat)
er nødvendig. Dette kan igjen illustreres tydelig med echo
som i @Calebs svar.
$ echo >(cat) /dev/fd/63
Så uten den ekstra >
, ville date >(cat)
være samme som date /dev/fd/63
som vil skrive ut en melding til stderr.
Anta at du har et program som bare tar filnavn som parametere og ikke behandler stdin
eller stdout
.Jeg vil bruke det oversimpliserte skriptet psub.sh
for å illustrere dette. Innholdet av psub.sh
er
#!/bin/bash [ -e "$1" -a -e "$2" ] && awk "{print $1}" "$1" > "$2"
I utgangspunktet tester det at begge argumentene er filer (ikke nødvendigvis vanlige filer), og hvis dette er tilfelle, skriv det første feltet i hver linje med "$1"
til "$2"
ved hjelp av awk. Så er en kommando som kombinerer alt som er nevnt så langt,
./psub.sh <(printf "a a\nc c\nb b") >(sort)
Dette vil skrives ut
a b c
og tilsvarer
printf "a a\nc c\nb b" | awk "{print $1}" | sort
men følgende fungerer ikke, og vi må bruke prosessubstitusjon her,
printf "a a\nc c\nb b" | ./psub.sh | sort
eller tilsvarende form
printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort
Hvis ./psub.sh
også står stdin
i tillegg til det som er nevnt ovenfor, eksisterer ikke en slik ekvivalent form, og i så fall er det ingenting vi kan bruke i stedet for prosesserstatning (selvfølgelig kan du også bruke en kalt pipe eller temp-fil, men det er en annen historie).
[[ -p <(date) ]] && echo true
. Dette girtrue
når jeg kjører det med bash 4.4 eller 3.2.