Minulla on kaksi prosessia ”>
jabar
, yhdistetty putkeen:
$ foo | bar
bar
poistuu aina 0: sta; Olen kiinnostunut foo
-koodin poistumiskoodista. Onko mitään keinoja päästä siihen?
Kommentit
- stackoverflow.com/questions/1221833/…
vastaus
bash
ja zsh
on taulukon muuttuja joka pitää sisällään viimeisen komentotulkin suorittaman putkilinjan jokaisen elementin (komennon) poistumistilan.
Jos käytät bash
, taulukkoa kutsutaan PIPESTATUS
(tapaus on tärkeä!) ja taulukon merkinnät alkavat nollasta:
$ false | true $ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}" 1 0
Jos käytät zsh
, taulukkoa kutsutaan nimellä pipestatus
(tapauskohtainen!) ja taulukkoindeksit alkavat yhdestä:
$ false | true $ echo "${pipestatus[1]} ${pipestatus[2]}" 1 0
Jos haluat yhdistää ne funktion sisällä tavalla, joka ei menetä arvoja:
$ false | true $ retval_bash="${PIPESTATUS[0]}" retval_zsh="${pipestatus[1]}" retval_final=$? $ echo $retval_bash $retval_zsh $retval_final 1 0
Suorita yllä oleva osio bash
tai zsh
ja saat samat tulokset; vain yksi ryhmistä retval_bash
ja retval_zsh
asetetaan. Toinen on tyhjä. Tämä antaisi funktion päättyä return $retval_bash $retval_zsh
(huomioi lainausmerkit!).
Kommentit
- Ja
pipestatus
zsh-muodossa. Valitettavasti muilla kuoreilla ei ole tätä ominaisuutta. - id = ”f0b2e640fe”>
ei ole tätä ominaisuutta.
echo "$pipestatus[1]" "$pipestatus[2]"
. if [ `echo "${PIPESTATUS[@]}" | tr -s ' ' + | bc` -ne 0 ]; then echo FAIL; fi
Vastaa
Siellä ovat 3 yleistä tapaa tehdä tämä:
putketiedostot
Ensimmäinen tapa on asettaa pipefail
-vaihtoehto (ksh
, zsh
tai bash
). Tämä on yksinkertaisin ja se, mitä se tekee, on periaatteessa asettaa poistumistila $?
viimeisen ohjelman poistumiskoodiksi poistuaksesi nollasta (tai nollasta, jos kaikki poistuvat onnistuneesti).
$ false | true; echo $? 0 $ set -o pipefail $ false | true; echo $? 1
$ PIPESTATUS
Bashilla on myös taulukon muuttuja nimeltä $PIPESTATUS
($pipestatus
in zsh
), joka sisältää kaikkien viimeisen putken ohjelmien poistumistilan.
$ true | true; echo "${PIPESTATUS[@]}" 0 0 $ false | true; echo "${PIPESTATUS[@]}" 1 0 $ false | true; echo "${PIPESTATUS[0]}" 1 $ true | false; echo "${PIPESTATUS[@]}" 0 1
Voit käyttää 3. komentoesimerkkiä saadaksesi tarvittavan arvon putkistoon.
Erota suoritukset
Tämä on vaikein ratkaisu. Suorita kukin komento erikseen ja sieppaa tila
$ OUTPUT="$(echo foo)" $ STATUS_ECHO="$?" $ printf "%s" "$OUTPUT" | grep -iq "bar" $ STATUS_GREP="$?" $ echo "$STATUS_ECHO $STATUS_GREP" 0 1
Kommentit
- Darn! Kirjoitin vain aiheesta PIPESTATUS.
- Tässä SO-kysymyksessä on useita muita tekniikoita: stackoverflow.com/questions/1221833/…
- @Patrick, että pipestatus-ratkaisu toimii bashilla, vain enemmän quastionia, jos käytän ksh-komentosarjaa, luuletko, että löydämme jotain vastaavaa kuin pipestatus? , (muistutan, että ksh ei tue pipestatusta)
- @yael En käytä ’ en käytä
ksh
, mutta lyhyesti katsomalla sitä ’ s -sivulle, se ei tue ’ t$PIPESTATUS
tai jotain vastaavaa. Se tukee kuitenkinpipefail
-vaihtoehtoa. - Päätin mennä pipefailin kanssa, koska sen avulla saan epäonnistuneen komennon tilan täältä:
LOG=$(failed_command | successful_command)
vastaus
Tämä ratkaisu toimii ilman bash-spesifisiä ominaisuuksia tai väliaikaisia tiedostoja . Bonus: lopulta poistumistila on itse asiassa poistumistila eikä jokin merkkijono tiedostossa.
Tilanne:
someprog | filter
sinä halua poistumistila kohteesta someprog
ja lähtö filter
.
Tässä on ratkaisuni:
((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
Tämän rakenteen tulos on vakio kohdasta filter
rakenteen vakiona ja poistumistila kohdasta someprog
rakenteen poistumistilana.
tämä rakenne toimii myös yksinkertaisella komentojen ryhmittelyllä {...}
alikuorien (...)
sijaan. alikuorilla on joitain vaikutuksia, muun muassa suorituskykykustannukset, joita emme tarvitse tässä. lue lisätietoja bash-käyttöoppaasta: https://www.gnu.org/software/bash/manual/html_node/Command-Grouping.html
{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; } } 4>&1
Valitettavasti bash-kielioppi vaatii välilyöntejä ja puolipisteitä kiharaisille aaltosulkeille, jotta rakenteesta tulee paljon tilavampi.
Käytän lopputekstissä alikuorimuunnosta. >
Esimerkki someprog
ja filter
:
someprog() { echo "line1" echo "line2" echo "line3" return 42 } filter() { while read line; do echo "filtered $line" done } ((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1 echo $?
Esimerkkilähtö:
filtered line1 filtered line2 filtered line3 42
Huomaa: aliprosessi perii avoimen tiedoston kuvaimet vanhemmalta. Tämä tarkoittaa, että someprog
perii avoimen tiedoston kuvaajat 3 ja 4. Jos someprog
kirjoittaa tiedostokuvaajaan 3, siitä tulee poistumistila. Todellinen poistumistila jätetään huomiotta, koska read
lukee vain kerran.
Jos olet huolissasi, että someprog
saattaa kirjoittaa tiedostokuvaaja 3 tai 4, on parasta sulkea tiedostokuvaajat ennen kuin kutsutaan someprog
.
(((((exec 3>&- 4>&-; someprog); echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
exec 3>&- 4>&-
ennen someprog
sulkee tiedostokuvaajan ennen someprog
suorittamista someprog
näitä tiedostokuvaajia ei yksinkertaisesti ole.
Se voidaan kirjoittaa myös seuraavasti: someprog 3>&- 4>&-
Rakenteen vaiheittainen selitys:
( ( ( ( someprog; #part6 echo $? >&3 #part5 ) | filter >&4 #part4 ) 3>&1 #part3 ) | (read xs; exit $xs) #part2 ) 4>&1 #part1
Alhaalta ylös:
- Alakoko luodaan tiedostokuvaajalla 4 ohjataan stdoutiin. Tämä tarkoittaa, että mikä tahansa alikuoren tiedostokuvaajaan 4 tulostettu, tulee koko rakenteen vakioksi.
- Luodaan putki ja vasemmalla olevat komennot (
#part3
) ja oikea (#part2
) suoritetaan.exit $xs
on myös putken viimeinen komento, mikä tarkoittaa, että merkkijono stdinistä on koko rakenteen poistumistila. - Alikuori luodaan tiedostolla descriptor 3 ohjataan standardiin. Tämä tarkoittaa sitä, että mikä tahansa tämän alikuoren tiedostokuvaajaan 3 tulostettu, päätyy
#part2
-kohtaan ja puolestaan on koko rakenteen poistumistila. - A putki luodaan ja komennot vasemmalla (
#part5
ja#part6
) ja oikealla (filter >&4
) suoritetaan. Tiedostonfilter
lähtö ohjataan tiedostokuvaimeen 4. Kohdassa#part1
tiedostokuvaaja 4 ohjataan stdout-tiedostoon. Tämä tarkoittaa, ettäfilter
-lähtö on koko rakenteen vakio. - Poistumistila kohdasta
#part6
tulostetaan tiedostokuvaajaan 3. Tiedostossa#part3
tiedostokuvaaja 3 ohjataan osoitteeseen#part2
. Tämä tarkoittaa, että poistumistila kohdasta#part6
on koko rakenteen lopullinen poistumistila. -
someprog
on teloitettu. Poistumistila otetaan#part5
. Stdout otetaan putkessa#part4
ja lähetetään edelleen osoitteeseenfilter
.filter
-lähtö puolestaan saavuttaa stdoutin, kuten#part4
kommenteissa h3 on selitetty. >
- Hienoa. Funktiolle voin vain tehdä
(read; exit $REPLY)
-
(exec 3>&- 4>&-; someprog)
yksinkertaistuu muotoon someprog 3>&- 4>&-
.
- Tämä menetelmä toimii myös ilman alikuoria:
{ { { { someprog 3>&- 4>&-; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; }; } 4>&1
(read; exit $REPLY)
(exec 3>&- 4>&-; someprog)
yksinkertaistuu muotoon someprog 3>&- 4>&-
. { { { { someprog 3>&- 4>&-; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; }; } 4>&1
Vastaa
Vaikka et olisikaan juuri sitä mitä pyysit, voit käyttää
#!/bin/bash -o pipefail
niin, että putkesi palauttavat viimeisen paluun, joka ei ole nolla.
koodausta saattaa olla vähän vähemmän
Muokkaa: Esimerkki
[root@localhost ~]# false | true [root@localhost ~]# echo $? 0 [root@localhost ~]# set -o pipefail [root@localhost ~]# false | true [root@localhost ~]# echo $? 1
Kommentit
-
set -o pipefail
komentosarjan sisällä tulisi olla vankempi, esim. jos joku suorittaa komentosarjanbash foo.sh
-palvelun kautta. - Kuinka se toimii? onko sinulla esimerkkiä?
- Huomaa, että
-o pipefail
ei ole POSIXissa. - Tämä ei toimi minun BASH 3.2.25: ssä ( 1) vapauttaa. / Tmp / ff: n yläosassa minulla on
#!/bin/bash -o pipefail
.Virhe on:/bin/bash: line 0: /bin/bash: /tmp/ff: invalid option name
- @FelipeAlvarez: Jotkin ympäristöt (mukaan lukien linux) eivät ’ t jäsennä välilyöntejä
#!
viivat ensimmäisen yli, joten tästä tulee/bin/bash
-o pipefail
/tmp/ff
tarvittavan/bin/bash
-o
pipefail
/tmp/ff
–getopt
(tai vastaava) jäsentäminen käyttämälläoptarg
, joka on seuraava kohde sarakkeessaARGV
, argumenttina-o
, joten se epäonnistuu. Jos tekisit kääreen (sanobash-pf
, joka juuri tekiexec /bin/bash -o pipefail "$@"
, ja laitat sen#!
-rivi, se toimisi. Katso myös: fi.wikipedia.org/wiki/Shebang_%28Unix%29
Vastaa
Mitä teen, kun mahdollista, syötän poistumistunnuksen kohdasta foo
kansioon bar
. Jos esimerkiksi tiedän, että foo
ei koskaan tuota riviä, jossa on vain numeroita, voin vain kiinnittää poistumiskoodin:
{ foo; echo "$?"; } | awk "!/[^0-9]/ {exit($0)} {…}"
Tai jos tiedän, että foo
-lähtö ei koskaan sisällä riviä, jossa on vain .
:
{ foo; echo .; echo "$?"; } | awk "/^\.$/ {getline; exit($0)} {…}"
Tämä voidaan tehdä aina, jos on olemassa jokin tapa saada bar
työskennellä kaikkien paitsi viimeisen rivin kanssa ja välitettävä viimeinen rivi poistumiskoodina.
Jos bar
on monimutkainen putki, jonka tuotoksen Älä tarvitse, voit ohittaa osan siitä tulostamalla poistumiskoodin toiseen tiedostokuvaajaan.
exit_codes=$({ { foo; echo foo:"$?" >&3; } | { bar >/dev/null; echo bar:"$?" >&3; } } 3>&1)
Tämän jälkeen $exit_codes
on yleensä foo:X bar:Y
, mutta se voi olla bar:Y foo:X
, jos bar
lopetetaan ennen kuin luet kaikki sen syötteet tai jos sinulla ei ole onnea. Luulen, että jopa 512 tavun pituisille kirjoituksille kirjoittaminen on atomisia kaikissa yksiköissä, joten foo:$?
ja bar:$?
osia ei sekoiteta kunhan merkkijonot ovat alle 507 tavua.
Jos sinun on siepattava lähtö bar
, se vaikeutuu. Voit yhdistää yllä olevat tekniikat järjestämällä bar
-lähdön tuloste ei koskaan sisällä riviä, joka näyttää siltä kuin poistumistunnus, mutta se saa hämmentävän.
output=$(echo; { { foo; echo foo:"$?" >&3; } | { bar | sed "s/^/^/"; echo bar:"$?" >&3; } } 3>&1) nl=" " foo_exit_code=${output#*${nl}foo:}; foo_exit_code=${foo_exit_code%%$nl*} bar_exit_code=${output#*${nl}bar:}; bar_exit_code=${bar_exit_code%%$nl*} output=$(printf %s "$output" | sed -n "s/^\^//p")
Ja tietysti on yksinkertainen vaihtoehto tilapäisen tiedoston käyttämiseen tilan tallentamiseen. Yksinkertainen, mutta ei niin yksinkertainen tuotannossa:
- Jos useita komentosarjoja on käynnissä samanaikaisesti tai jos sama komentosarja käyttää tätä menetelmää useissa paikoissa, sinun on tehtävä varmista, että he käyttävät erilaisia väliaikaisia tiedostojen nimiä.
- Väliaikaisen tiedoston luominen turvallisesti jaettuun hakemistoon on vaikeaa. Usein
/tmp
on ainoa paikka, jossa komentosarja pystyy varmasti kirjoittamaan tiedostoja. Käytämktemp
, joka ei ole POSIX, mutta on nykyään käytettävissä kaikissa vakavissa yksiköissä.
foo_ret_file=$(mktemp -t) { foo; echo "$?" >"$foo_ret_file"; } | bar bar_ret=$? foo_ret=$(cat "$foo_ret_file"; rm -f "$foo_ret_file")
Kommentit
- Kun käytän väliaikaista tiedostotapaa, haluan lisätä EXITiin ansan, joka poistaa kaikki väliaikaiset tiedostot jotta roskia ei jää, vaikka komentosarja kuolisi
Vastaa
Aloitetaan putkesta:
foo | bar | baz
Tässä on yleinen ratkaisu, joka käyttää vain POSIX-komentotulkkia eikä väliaikaisia tiedostoja:
exec 4>&1 error_statuses="`((foo || echo "0:$?" >&3) | (bar || echo "1:$?" >&3) | (baz || echo "2:$?" >&3)) 3>&1 >&4`" exec 4>&-
$error_statuses
sisältää epäonnistuneiden prosessien tilakoodit satunnaisessa järjestyksessä, indeksit kertovat, mikä komento lähettää kunkin tilan.
# if "bar" failed, output its status: echo "$error_statuses" | grep "1:" | cut -d: -f2 # test if all commands succeeded: test -z "$error_statuses" # test if the last command succeeded: ! echo "$error_statuses" | grep "2:" >/dev/null
Huomaa testeissä lainaukset $error_statuses
-kohdan ympärillä; ilman niitä grep
ei voi erota, koska uudet viivat pakotetaan välilyönteihin.
Vastaa
Jos sinulla on asennettuna moreutils -paketti, voit käyttää mispipe -apuohjelmaa, joka tekee juuri sen, mitä pyysit.
vastaus
Joten halusin antaa vastauksen, kuten lesmana, mutta mielestäni minun on ehkä hieman yksinkertaisempi ja hieman edullisempi puhdas- Bourne-shell-ratkaisu:
# You want to pipe command1 through command2: exec 4>&1 exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1` # $exitstatus now has command1"s exit status.
Mielestäni tämä selitetään parhaiten sisältä ulospäin – command1 suorittaa ja tulostaa säännöllisen tuloksensa stdout-tiedostoon (tiedostokuvaaja 1) , sitten kun se on valmis, printf suorittaa ja tulostaa komento1: n poistumiskoodin vakiona, mutta se ohjataan tiedostokuvaajaan 3.
Komennon1 ollessa käynnissä sen vakio lähetetään command2 (printf: n lähtö ei koskaan pääse komentoon2, koska lähetämme sen tiedostokuvaajaan 3 1: n sijaan, s mitä putki lukee).Sitten ohjaamme komento2: n lähdön tiedostokuvaajaan 4 niin, että se pysyy myös tiedostokuvaajan 1 ulkopuolella – koska haluamme tiedostokuvaajan 1 ilmaiseksi hieman myöhemmin, koska tuomme tiedostokuvaajan 3 tulostusjohdon takaisin alas tiedostokuvaaja 1 – koska komento korvaa (backticks) sieppaa sen ja muuttuja sijoittuu tähän.
Viimeinen taikuuden bitti on se, että ensimmäinen exec 4>&1
teimme erillisenä komentona – se avaa tiedostokuvaajan 4 kopiona ulkoisen kuoren vakiotiedostosta. Komennon korvaaminen sieppaa kaiken, mikä on standardille kirjoitettu, sen sisällä olevien komentojen näkökulmasta – mutta koska command2: n lähtö tulee tiedostokuvaajaan 4 komentojen korvaamisen osalta, komentojen korvaaminen ei sieppaa niitä – Kuitenkin, kun se ”poistuu” komentokorvauksesta, se menee silti komentosarjan yleiseen tiedostokuvaajaan 1.
(exec 4>&1
on olla erillinen komento, koska monet tavalliset komentotulkit eivät pidä siitä, kun yrität kirjoittaa tiedostokuvaajaan komennon korvikkeen sisällä, joka avataan korvausta käyttävässä ”ulkoisessa” -komennossa. Tämä on siis yksinkertaisin kannettava tapa tehdäksesi sen.)
Voit tarkastella sitä vähemmän teknisellä ja leikkisemmällä tavalla, ikään kuin komentojen lähdöt hämmentävät toisiaan: komento1 antaa komennon2, sitten printf: n tulos hyppää komennon 2 yli niin, että komento2 ei tartu siihen, ja sitten komennon 2 tulos hyppää yli ja ulos Komennon korvaaminen samalla tavalla kuin printf laskeutuu juuri ajoissa saadakseen vaihdon siepatuksi niin, että se päätyy muuttujaan, ja command2: n lähtö jatkuu iloisella tavallaan kirjoitettaessa vakiotulosteeseen aivan kuten tavallisessa putkessa. / p>
Kuten ymmärrän, $?
sisältää edelleen toisen putken komennon palautuskoodin, koska muuttujien määritykset, komentojen korvaukset ja yhdistetyt komennot ovat kaikki läpinäkyvästi niiden sisällä olevan komennon palautuskoodille, joten command2: n palautustilan pitäisi levitä – mielestäni tämä saattaa olla jonkin verran parempi ratkaisu kuin ehdotettu, eikä tarvita määritellä lisäfunktiota. jonka on kirjoittanut lesmana.
Varoitusten mukaisesti lesmana mainitsee, että on mahdollista, että command1 pääsee jossain vaiheessa käyttämään tiedostokuvaajia 3 tai 4, joten voidaksesi olla vankempi, tekisit:
exec 4>&1 exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1` exec 4>&-
Huomaa, että käytän esimerkissä yhdistekomentoja, mutta alikuoria (käyttäen ( )
{ }
-kohdan sijaan toimii myös, vaikka se voi olla ehkä vähemmän tehokasta.)
Komennot perivät tiedostokuvaajat prosessista joka käynnistää ne, joten koko toinen rivi perii tiedostokuvaajan neljä ja yhdistekomento, jota seuraa 3>&1
, perii tiedostokuvaajan kolme. Joten 4>&-
varmistaa, että sisempi yhdistelmäkomento ei peri tiedostokuvaajaa neljä ja 3>&-
ei peri tiedostokuvaajaa kolme, joten command1 saa ”puhtaamman”, vakiomaisemman ympäristön. Voit myös siirtää sisäistä 4>&-
3>&-
-kohdan viereen, mutta ymmärrän, miksi ei vain rajoittaa sen soveltamisalaa mahdollisimman paljon.
En ole varma, kuinka usein asiat käyttävät tiedostokuvaajaa 3 ja 4 suoraan – luulen, että useimmiten ohjelmat käyttävät syscalleja, jotka palauttavat tällä hetkellä käyttämättömät tiedostokuvaajat, mutta joskus koodi kirjoittaa tiedostoon kuvaaja 3, luulisin (voisin kuvitella ohjelman tarkistavan tiedostokuvaajan, onko se auki, ja käyttää sitä, jos se on, tai käyttäytyy eri tavalla vastaavasti, jos se ei ole). Joten jälkimmäinen on todennäköisesti paras pitää mielessä ja käyttää yleiskäyttöisissä tapauksissa.
Kommentit
- Näyttää mielenkiintoiselta, mutta voin ’ t aivan selvittää, mitä odotat tämän komennon tekevän, ja tietokoneeni voi ’ t joko; saan
-bash: 3: Bad file descriptor
. - @ G-Man Oikea, unohdan jatkuvasti, että bashilla ei ole aavistustakaan, mitä se ’ tekee, kun se toimii Toisin kuin tavallisesti käyttämäni kuoret (busyboxin mukana tuleva tuhka). ’ ilmoitan sinulle, kun ajattelen kiertotapaa, joka tekee bashista onnellisen. Sillä välin, jos sinulla ’ on Debian-laatikko kätevä, voit kokeilla sitä viivalla tai jos ’ sinulla on busybox kätevä sinulle voi kokeilla sitä busybox ash / sh -laitteella.
- @ G-Man Mitä odotan komennon tekevän ja mitä se tekee muissa kuorissa, on ohjata stdout komentosta1, joten se ei ’ ei tartu komentojen korvaamiseen, mutta kun se on komennon korvaamisen ulkopuolella, se pudottaa fd3: n takaisin stdoutiin, joten se ’ lähetetään odotetusti komentoon2.Kun komento1 poistuu, printf käynnistää ja tulostaa sen poistumistilan, jonka komento korvaa sieppaamalla muuttujaan. Erittäin yksityiskohtainen erittely tässä: stackoverflow.com/questions/985876/tee-and-exit-status/… myös , tämä kommenttisi kuului ikään kuin sen olisi tarkoitus olla tavallaan loukkaavaa?
- Mistä aloitan? (1) Olen pahoillani, jos sinusta tuntui loukkaantuneen. ”Näyttää mielenkiintoiselta” tarkoitettiin hartaasti; olisi hienoa, jos jotain niin pienikokoista kuin se toimisi yhtä hyvin kuin odotitkin. Sen lisäksi sanoin yksinkertaisesti, etten ymmärtänyt, mitä ratkaisusi pitäisi tehdä. Olen työskennellyt / pelannut Unixin kanssa pitkään aikaa (ennen kuin Linux oli olemassa), ja jos en ymmärrä jotain, se on punainen lippu, joka ehkä muut ihmiset eivät myöskään ymmärrä sitä ja että se tarvitsee lisää selitystä (IMNSHO). … (Jatkuu)
- (jatkuu) … Koska sinä ”… haluat ajatella… että [sinä] ymmärrät melkein kaikkea muuta kuin tavallinen ihminen ”, ehkä sinun pitäisi muistaa, että Stack Exchangen tavoitteena ei ole olla komentojen kirjoittamispalvelu, joka tuottaa tuhansia kertaluonteisia ratkaisuja triviaalisesti erillisiin kysymyksiin; vaan pikemminkin opettaa ihmisiä ratkaisemaan omat ongelmansa. Ja tätä varten sinun on ehkä selitettävä asioita riittävän hyvin, jotta ”keskimääräinen ihminen” ymmärtäisi ne. Katso lesmanan vastauksesta esimerkki erinomaisesta selityksestä. … (Jatkuu)
vastaus
lesmanan yllä oleva ratkaisu voidaan tehdä myös ilman aloittaa sisäkkäiset aliprosessit käyttämällä sen sijaan { .. }
(muista, että tämän ryhmän ryhmien komentojen muoto on aina lopetettava puolipisteillä). Jotain tällaista:
{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | stdintoexitstatus; } 4>&1
Olen tarkistanut tämän rakenteen viivaversioilla 0.5.5 ja bash-versioilla 3.2.25 ja 4.2.42, joten vaikka jotkin kuoret eivät tue { .. }
ryhmittely, se on edelleen POSIX-yhteensopiva.
Kommentit
- Tämä toimii todella hyvin useimpien I ’ ve kokeillut sitä, mukaan lukien NetBSD sh, pdksh, mksh, dash, bash. Voin kuitenkin ’ saada sen toimimaan AT & T Ksh (93s +, 93u +) tai zsh (4.3.9, 5.2), vaikka
set -o pipefail
ksh: na tai mikä tahansa määrä siroteltujawait
-komennot kummassakin. Luulen, että se voi osittain ainakaan, ole ksh: n jäsentämisongelma, ikään kuin pitäisinkin kiinni alikuorien käytöstä, se toimii hyvin, mutta jopaif
-asetuksella voit valita ksh: n alikuorimuunnoksen, mutta jätä yhdistetyt komennot muille se epäonnistuu.
Vastaus
Seuraaminen on tarkoitettu lisäykseksi @Patrikin vastaukseen, jos et pysty käyttämään jotakin tavallisimmista ratkaisuista.
Tämä vastaus edellyttää seuraavaa:
- Sinulla on kuori, joka ei tiedä
$PIPESTATUS
eikäset -o pipefail
- Haluat käyttää putkea rinnakkaiseen suoritukseen, joten ei väliaikaisia tiedostoja.
- Sinä älä halua ylimääräistä sotkua ympärilläsi, jos keskeytät komentosarjan mahdollisesti äkillisen virtakatkoksen takia.
- Tämän ratkaisun on oltava suhteellisen helppo seurata ja puhdas lukea.
- Teet et halua ottaa käyttöön uusia alikuoria.
- Et voi hioa olemassa olevia tiedostokuvaajia, joten stdin / out / err ei saa koskettaa (miten voit ottaa väliaikaisesti käyttöön uuden).
Lisäoletuksia. Voit päästä eroon kaikista, mutta tämä ryöstää reseptiä liikaa, joten sitä ei käsitellä tässä:
- Kaikki mitä haluat tietää, on, että kaikilla PIPE-komennoilla on poistumiskoodi 0.
- Et tarvitse ylimääräisiä sivutaajuustietoja.
- Kuori odottaa kaikkien putkikomentojen palauttamista.
Ennen: foo | bar | baz
, mutta tämä palauttaa vain viimeisen komennon (baz
) poistumiskoodin
Etsitään: $?
ei saa olla 0
(true), jos jokin putken komennoista epäonnistui
Jälkeen:
TMPRESULTS="`mktemp`" { rm -f "$TMPRESULTS" { foo || echo $? >&9; } | { bar || echo $? >&9; } | { baz || echo $? >&9; } #wait ! read TMPRESULTS <&8 } 9>>"$TMPRESULTS" 8<"$TMPRESULTS" # $? now is 0 only if all commands had exit code 0
Selitetty:
- Lämpötilatiedosto luodaan
mktemp
. Tämä luo yleensä välittömästi tiedoston tiedostoon/tmp
- Tämä temp-tiedosto ohjataan sitten FD 9: een kirjoitusta varten ja FD 8: aa lukemaan
- Sitten tempfile poistetaan välittömästi. Se pysyy kuitenkin auki, kunnes molemmat FD: t lakkaavat olemasta.
- Nyt putki on käynnistetty. Jokainen vaihe lisää vain FD 9: een, jos tapahtui virhe.
-
wait
tarvitaanksh
, koskaksh
muu ei odota kaikkien putkikomentojen päättymistä. Huomaa kuitenkin, että joissakin taustatehtävissä on ei-toivottuja sivuvaikutuksia, joten kommentoin sitä oletuksena.Jos odotus ei vahingoita, voit kommentoida sitä. - Myöhemmin tiedoston sisältö luetaan. Jos se on tyhjä (koska kaikki toimivat)
read
palauttaafalse
, jotentrue
osoittaa virheen
Tätä voidaan käyttää laajennuksen korvaajana yksi komento ja vaatii vain seuraavaa:
- Käyttämättömät FD: t 9 ja 8
- Yksi ympäristömuuttuja, joka pitää sisällään temp-tiedoston.
- Ja tämä resepti voidaan mukauttaa melkein mihin tahansa olemassa olevaan kuoreen, joka sallii IO-uudelleenohjauksen
- Lisäksi se on melko alustan agnostinen eikä tarvitse esimerkiksi
/proc/fd/N
BUGIT:
Tässä komentosarjassa on vika, jos /tmp
-tila loppuu. Jos tarvitset suojaa myös tätä keinotekoista tapausta vastaan, voit tehdä sen seuraavasti, mutta tällä on kuitenkin haitta, että 0
-määrä 000
-kohdassa riippuu komennon määrästäputki, joten se on hieman monimutkaisempi:
TMPRESULTS="`mktemp`" { rm -f "$TMPRESULTS" { foo; printf "%1s" "$?" >&9; } | { bar; printf "%1s" "$?" >&9; } | { baz; printf "%1s" "$?" >&9; } #wait read TMPRESULTS <&8 [ 000 = "$TMPRESULTS" ] } 9>>"$TMPRESULTS" 8<"$TMPRESULTS"
Siirrettävyyshuomautukset:
-
ksh
ja vastaavat kuoret, jotka odottavat vain viimeistä putkikomentoa, tarvitsevatwait
kommentoimatta -
Viimeinen esimerkki käyttää
printf "%1s" "$?"
echo -n "$?"
-kohdan sijaan, koska tämä on kannettavampi. Kaikkia alustoja ei tulkita-n
oikein. -
printf "$?"
tekisi sen myös,printf "%1s"
saa kuitenkin kiinni joitain kulmatapauksia, jos suoritat komentosarjan jollakin todella rikki alustalla. (Lue: Jos satut ohjelmoimaan ryhmässäparanoia_mode=extreme
.) -
FD 8 ja FD 9 voivat olla korkeammat alustoilla, jotka tukevat useita numeroa. AFAIR a POSIX -yhteensopivan kuoren ei tarvitse tukea vain yksinumeroisia numeroita.
-
Testattu Debian 8.2 -käyttöjärjestelmällä
sh
,bash
,ksh
,ash
,sash
ja jopacsh
vastaus
Tämä on kannettava eli toimii minkä tahansa POSIX-yhteensopivan kuoren kanssa, ei vaadi nykyisen hakemiston olevan kirjoitettavissa ja sallii useiden samaa temppua käyttävien komentosarjojen suorittamisen samanaikaisesti.
(foo;echo $?>/tmp/_$$)|(bar;exit $(cat /tmp/_$$;rm /tmp/_$$))
Muokkaa: tässä on vahvempi versio, joka seuraa Gillesin kommentteja:
(s=/tmp/.$$_$RANDOM;((foo;echo $?>$s)|(bar)); exit $(cat $s;rm $s))
Edit2: ja tässä on hieman kevyempi muunnelma epäilyttävän jim-kommentin jälkeen:
(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s))
Kommentit
- Tämä ei toimi ’ ei useista syistä. 1. Väliaikainen tiedosto voidaan lukea ennen kuin se ’ kirjoitetaan. 2. Väliaikaisen tiedoston luominen jaettavaan hakemistoon, jonka nimi on ennustettavissa, on epävarmaa (triviaali DoS, symlink-kilpailu). 3. Jos sama komentosarja käyttää tätä temppua useita kertoja, se ’ käyttää aina samaa tiedostonimeä. Ratkaise 1 lukemalla tiedosto putkilinjan valmistuttua. Ratkaise 2 ja 3 käyttämällä väliaikaista tiedostoa, jonka nimi on sattumanvaraisesti luotu tai yksityisessä hakemistossa.
- +1 No, $ {PIPESTATUS [0]} on helpompaa, mutta perusajatus toimii tässä, jos tiedät ongelmista, jotka Gilles mainitsee.
- Voit tallentaa muutaman alikuoren:
(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s))
. @Johan: Olen samaa mieltä siitä, että ’ on helpompaa Bashin kanssa, mutta joissakin yhteyksissä Bashin välttäminen on sen arvoista.
Vastaa
Hieman varovaisesti tämän pitäisi toimia:
foo-status=$(mktemp -t) (foo; echo $? >$foo-status) | bar foo_status=$(cat $foo-status)
Kommentit
- Entä siivoaminen kuten jlliagre? Älä ’ jätä tiedostoa nimeltään foo-status?
- @Johan: Jos haluat mieluummin ehdotukseni, älä ’ t epäröi äänestää siitä;) Sen lisäksi, että se ei jätä tiedostoa, sillä on se etu, että se sallii useiden prosessien suorittaa tämän samanaikaisesti, eikä nykyisen hakemiston tarvitse olla kirjoitettavissa.
vastaus
Seuraava ”if” -lohko toimii vain, jos ”komento” onnistui:
if command; then # ... fi
Tarkemmin sanottuna voit suorittaa jotain tällaista:
haconf_out=/path/to/some/temporary/file if haconf -makerw > "$haconf_out" 2>&1; then grep -iq "Cluster already writable" "$haconf_out" # ... fi
Mikä suoritetaan haconf -makerw
ja tallenna sen stdout ja stderr kansioon ”$ haconf_out”. Jos palautettu arvo kohteesta haconf
on tosi, ”if” -lohko suoritetaan ja grep
lukee ”$ haconf_out” yrittäen vastaamaan sitä ”Cluster már kirjoitettavissa”.
Huomaa, että putket puhdistavat itsensä automaattisesti; uudelleenohjauksen kanssa sinun on oltava varovainen poistaaksesi kohteen ”$ haconf_out”, kun se on tehty.
Ei niin tyylikäs kuin pipefail
, mutta oikeutettu vaihtoehto, jos tämä toiminto ei ole ulottuvilla.
Vastaa
Alternate example for @lesmana solution, possibly simplified. Provides logging to file if desired. ===== $ cat z.sh TEE="cat" #TEE="tee z.log" #TEE="tee -a z.log" exec 8>&- 9>&- { { { { #BEGIN - add code below this line and before #END ./zz.sh echo ${?} 1>&8 # use exactly 1x prior to #END #END } 2>&1 | ${TEE} 1>&9 } 8>&1 } | exit $(read; printf "${REPLY}") } 9>&1 exit ${?} $ cat zz.sh echo "my script code..." exit 42 $ ./z.sh; echo "status=${?}" my script code... status=42 $
Vastaa
(Ainakin bashilla) yhdistettynä set -e
-sivuun voidaan käyttää alikuorea emuloimaan nimenomaisesti putketusta ja poistumaan putkivirheestä
set -e foo | bar ( exit ${PIPESTATUS[0]} ) rest of program
Joten jos foo
epäonnistuu jostain syystä – muuta ohjelmaa ei suoriteta ja komentosarja poistuu vastaavalla virhekoodilla. (Tämä olettaa, että foo
tulostaa oman virheen, joka riittää ymmärtämään epäonnistumisen syyn)
Vastaa
MUOKKAA : Tämä vastaus on väärä, mutta mielenkiintoinen, joten jätän sen tulevaisuuteen viite.
!
lisääminen komentoon kääntää palautuskoodin.
http://tldp.org/LDP/abs/html/exit-status.html
# =========================================================== # # Preceding a _pipe_ with ! inverts the exit status returned. ls | bogus_command # bash: bogus_command: command not found echo $? # 127 ! ls | bogus_command # bash: bogus_command: command not found echo $? # 0 # Note that the ! does not change the execution of the pipe. # Only the exit status changes. # =========================================================== #
kommentit
- Luulen, että tämä ei liity. Haluan esimerkissäsi tietää
ls
-lähtökoodin, ei kääntääbogus_command
- Ehdotan, että vedän vastauksen takaisin.
- No, näyttää siltä, että olen
m idiootti. I ’ olemme tosiasiallisesti käyttäneet tätä komentosarjassa ennen kuin ajattelin, että se teki sen, mitä OP halusi. Hyvä asia, että en käyttänyt sitä ’ li>