Hae poistumistila prosessista, joka ' lähettää toiseen

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

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.

  • Huomaa: zsh: n matriisit alkavat vasta-aiheisesti hakemistosta 1, joten ’ s echo "$pipestatus[1]" "$pipestatus[2]".
  • Voisit tarkistaa koko putken seuraavasti: if [ `echo "${PIPESTATUS[@]}" | tr -s ' ' + | bc` -ne 0 ]; then echo FAIL; fi
  • @JanHudec: Ehkä sinun pitäisi lukea vastaukseni viisi ensimmäistä sanaa. Osoita myös ystävällisesti, mihin kysymykseen pyydettiin vain POSIX-vastausta.
  • @JanHudec: Sitä ei myöskään merkitty POSIX-tunnisteella. Miksi luulet vastauksen olevan POSIX? Sitä ei ollut määritelty, joten annoin pätevän vastauksen. Vastauksessani ei ole mitään virheellistä, ja muita tapauksia varten on useita vastauksia.
  • 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 kuitenkin pipefail -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:

    1. Alakoko luodaan tiedostokuvaajalla 4 ohjataan stdoutiin. Tämä tarkoittaa, että mikä tahansa alikuoren tiedostokuvaajaan 4 tulostettu, tulee koko rakenteen vakioksi.
    2. 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.
    3. 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.
    4. A putki luodaan ja komennot vasemmalla (#part5 ja #part6) ja oikealla (filter >&4) suoritetaan. Tiedoston filter lähtö ohjataan tiedostokuvaimeen 4. Kohdassa #part1 tiedostokuvaaja 4 ohjataan stdout-tiedostoon. Tämä tarkoittaa, että filter -lähtö on koko rakenteen vakio.
    5. 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.
    6. someprog on teloitettu. Poistumistila otetaan #part5. Stdout otetaan putkessa #part4 ja lähetetään edelleen osoitteeseen filter. 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

    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 komentosarjan bash 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/ffgetopt (tai vastaava) jäsentäminen käyttämällä optarg, joka on seuraava kohde sarakkeessa ARGV, argumenttina -o, joten se epäonnistuu. Jos tekisit kääreen (sano bash-pf, joka juuri teki exec /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ä siroteltuja wait -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 jopa if -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 tarvitaan ksh, koska ksh 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 palauttaa false, joten true 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, tarvitsevat wait 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 jopa csh

    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>

    Vastaa

    Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *