Annak a folyamatnak a kilépési állapota, amelyet ' egy másik

Két folyamatom van foo és bar, csővel összekötve:

$ foo | bar 

bar mindig kilép 0-ból; “foo kilépési kódja érdekel. Van-e valamilyen módja ennek eljutására?

Megjegyzések

Válasz

bash és zsh tömb változóval rendelkezik amely megtartja a shell által végrehajtott utolsó folyamat minden elemének (parancsának) kilépési állapotát.

Ha a bash fájlt használja, a tömböt PIPESTATUS (az eset számít!), és a tömb jelzései nulláról indulnak:

$ false | true $ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}" 1 0 

Ha a zsh, a tömböt pipestatus -nek hívják (az eset számít!), és a tömbindexek egyből indulnak:

$ false | true $ echo "${pipestatus[1]} ${pipestatus[2]}" 1 0 

Ha egy funkcióban olyan módon kívánja őket kombinálni, amely nem veszíti el az értékeket:

$ false | true $ retval_bash="${PIPESTATUS[0]}" retval_zsh="${pipestatus[1]}" retval_final=$? $ echo $retval_bash $retval_zsh $retval_final 1 0 

Futtassa a fentieket a bash vagy zsh és ugyanazokat az eredményeket kapja; a retval_bash és a retval_zsh közül csak az egyik lesz beállítva. A másik üres lesz. Ez lehetővé tenné, hogy a függvény return $retval_bash $retval_zsh -vel fejeződjön be (vegye figyelembe az idézőjelek hiányát!).

Megjegyzések

  • És pipestatus a zsh-ben. Sajnos más héjak nem ‘ nem rendelkeznek ezzel a funkcióval.
  • Megjegyzés: A zsh tömbök ellenintuitív módon az 1. indexnél kezdődnek, tehát ‘ s echo "$pipestatus[1]" "$pipestatus[2]".
  • A teljes csővezetéket így ellenőrizheti: if [ `echo "${PIPESTATUS[@]}" | tr -s ' ' + | bc` -ne 0 ]; then echo FAIL; fi
  • @JanHudec: Talán el kellene olvasnod a válaszom első öt szavát. Kérjük, szíveskedjen felhívni a figyelmet arra is, hogy a kérdés csak POSIX-os választ igényelt-e.
  • @JanHudec: A POSIX címkével sem volt ellátva. Miért feltételezi, hogy a válasz POSIX legyen? Nem volt megadva, ezért minősített választ adtam. Nincs semmi helytelen a válaszomban, ráadásul több válasz is rendelkezésre áll más esetek kezelésére.

Válasz

Ott ennek 3 leggyakoribb módja van:

Pipefail

Az első módszer a pipefail opció beállítása (ksh, zsh vagy bash). Ez a legegyszerűbb, és amit csinál, az alapvetően a $? kilépési állapotot állítja be az utolsó program kilépési kódjára, hogy kilépjen nulla (vagy nulla, ha mindegyik sikeresen kilépett).

$ false | true; echo $? 0 $ set -o pipefail $ false | true; echo $? 1 

$ PIPESTATUS

A Bash rendelkezik egy $PIPESTATUS ($pipestatus in zsh), amely tartalmazza az utolsó folyamat összes programjának kilépési állapotát.

$ true | true; echo "${PIPESTATUS[@]}" 0 0 $ false | true; echo "${PIPESTATUS[@]}" 1 0 $ false | true; echo "${PIPESTATUS[0]}" 1 $ true | false; echo "${PIPESTATUS[@]}" 0 1 

A 3. parancs példájával megkaphatja a szükséges értéket a folyamatban.

Végrehajtások különválasztása

Ez a megoldások közül a legkellemetlenebb. Futtassa az egyes parancsokat külön, és rögzítse az állapotot

$ OUTPUT="$(echo foo)" $ STATUS_ECHO="$?" $ printf "%s" "$OUTPUT" | grep -iq "bar" $ STATUS_GREP="$?" $ echo "$STATUS_ECHO $STATUS_GREP" 0 1 

Megjegyzések

  • Darn! Éppen a PIPESTATUS-ról írtam.
  • Referenciaként számos más technikát tárgyalunk ebben a SO kérdésben: stackoverflow.com/questions/1221833/…
  • @Patrick a pipestatus megoldás a bash-on működik, csak még több kérdés, ha ksh szkriptet használok, szerinted találhatunk valami hasonlót a pipestatushoz? , (emlékezetemben látom, hogy a pipstatus a ksh által nem támogatott)
  • @yael nem használok ‘ nem használok ksh de egy rövid pillantást vetve rá ‘ s manp, ‘ nem támogatja a $PIPESTATUS vagy bármi hasonló. Ugyanakkor támogatja a pipefail opciót.
  • Úgy döntöttem, hogy a pipefail mellett haladok, mivel itt megadhatom a sikertelen parancs állapotát: LOG=$(failed_command | successful_command)

Válasz

Ez a megoldás bash specifikus funkciók vagy ideiglenes fájlok használata nélkül működik . Bónusz: végül a kilépési állapot valójában kilépési állapot, és nem egy karakterlánc a fájlban.

Helyzet:

someprog | filter 

Ön a kilépési állapotot a someprog és a filter kimenetet szeretné kérni.

Íme a megoldás:

((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1 

Ennek a konstrukciónak az eredménye a filter stdout, mint a konstrukció stdoutja, a a konstrukció kilépési állapotaként.


ez a konstrukció a {...} egyszerű parancscsoportosítással is működik alhéjak helyett (...). Az alhéjaknak vannak bizonyos következményei, többek között egy teljesítményköltség, amelyre itt nincs szükségünk. további részletekért olvassa el a finom bash kézikönyvet: 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 

Sajnos a bash nyelvtan szóközöket és pontosvesszőket igényel a göndör zárójelek számára, így a konstrukció sokkal tágasabbá válik.

A szöveg további részében az alhéj változatát fogom használni.


Példa someprog és 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 $? 

Kimeneti példa:

filtered line1 filtered line2 filtered line3 42 

Megjegyzés: a gyermekfolyamat örökli a nyitott fájlleírókat a szülőtől. Ez azt jelenti, hogy someprog örökölni fogja a 3. és 4. nyitott fájlleírót. Ha a someprog a 3. fájlleíróba ír, akkor ez kilépési állapot lesz. A valós kilépési állapot figyelmen kívül marad, mert a read csak egyszer olvas.

Ha attól tart, hogy a someprog írhat a 3. vagy 4. leíró fájlba helyezéséhez a legjobb, ha a someprog hívása előtt bezárja a fájlleírókat.

(((((exec 3>&- 4>&-; someprog); echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1 

exec 3>&- 4>&- a someprog előtt bezárja a fájlleírót, mielőtt végrehajtaná a someprog parancsot, tehát someprog ezek a fájlleírók egyszerűen nem léteznek.

Ez is így írható: someprog 3>&- 4>&-


A konstrukció lépésről lépésre történő magyarázata:

( ( ( ( someprog; #part6 echo $? >&3 #part5 ) | filter >&4 #part4 ) 3>&1 #part3 ) | (read xs; exit $xs) #part2 ) 4>&1 #part1 

Alulról felfelé:

  1. Alhéj jön létre fájlleíróval 4 átirányítva a stdout-ra. Ez azt jelenti, hogy bármi, amit az alhéjban a 4. fájlleíróba nyomtatnak, a teljes konstrukció stdout-jává válik.
  2. Létrejön egy cső, és a bal oldali parancsok (#part3) és a right (#part2) végrehajtásra kerül. Az exit $xs egyúttal a pip utolsó parancsa, ami azt jelenti, hogy az stdin karakterlánc az egész konstrukció kilépési állapota lesz.
  3. Az alhéj fájllal jön létre 3. leíró átirányítva stdout-ra. Ez azt jelenti, hogy bármi, amit ebben az alhéjban a 3. fájlleíróba nyomtatnak, #part2 be fog kerülni, és ez az egész konstrukció kilépési állapota lesz.
  4. A létrehozza a pipát, és a bal oldali (#part5 és #part6) és jobb (filter >&4) végrehajtják. Az filter kimenetét átirányítják a 4 fájlleíróba. A #part1 fájlban a 4 fájlleírót átirányították a stdout fájlba. Ez azt jelenti, hogy az filter kimenete a teljes konstrukció stdout-ja.
  5. Kilépési állapot #part6 -ből kinyomtatva a 3. fájlleíróba. A #part3 fájlban a 3. fájlleírót átirányították a #part2 fájlba. Ez azt jelenti, hogy az #part6 parancsból származó kilépési állapot lesz a teljes konstrukció végső kilépési állapota.
  6. someprog végrehajtott. A kilépési állapot a #part5 fájlba kerül. Az stdout-ot az #part4 cső veszi át, és továbbítja az filter címre. Az filter kimenete viszont eléri az stdout-ot, amint azt a #part4

megjegyzések

  • Szép. Lehet, hogy csak azt a funkciót hajtom végre, (read; exit $REPLY)
  • (exec 3>&- 4>&-; someprog) egyszerűsödik someprog 3>&- 4>&-.
  • Ez a módszer alhéjak nélkül is működik: { { { { someprog 3>&- 4>&-; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; }; } 4>&1

Válasz

Bár nem pontosan az, amit kértél, használhatod a

#!/bin/bash -o pipefail 

parancsot, hogy a csövek visszaadják az utolsó nem nulla visszatérést.

lehet, hogy valamivel kevesebb kódolás van

Szerkesztés: Példa

[root@localhost ~]# false | true [root@localhost ~]# echo $? 0 [root@localhost ~]# set -o pipefail [root@localhost ~]# false | true [root@localhost ~]# echo $? 1 

Megjegyzések

  • set -o pipefail a szkripten belül robusztusabbnak kell lennie, pl. ha valaki végrehajtja a szkriptet a bash foo.sh segítségével.
  • Hogyan működik? van példád?
  • Vegye figyelembe, hogy a -o pipefail nincs a POSIX rendszerben.
  • Ez nem működik a BASH 3.2.25 ( 1) -engedés. A / tmp / ff tetején van #!/bin/bash -o pipefail.A hiba a következő: /bin/bash: line 0: /bin/bash: /tmp/ff: invalid option name
  • @FelipeAlvarez: Egyes környezetek (beleértve a linuxot is) nem elemzik a ‘ szóközöket id = “35034b70b2”>

vonalak az elsőn túl, így ez/bin/bash-o pipefail/tmp/ff, a szükséges/bin/bash-opipefail/tmp/ffgetopt(vagy hasonló) elemzés aoptargsegítségével, amely a következő elem aARGV, a-oargumentumaként, tehát nem sikerül. Ha csomagolást készítene (mondjuk,bash-pf, amely éppexec /bin/bash -o pipefail "$@", és ezt tegye a sor, ez működne. Lásd még: hu.wikipedia.org/wiki/Shebang_%28Unix%29

Válasz

Amit lehetséges, az az, hogy a kilépési kódot betáplálom a foo könyvtárba a bar. Például, ha tudom, hogy a foo soha nem hoz létre csak számjegyből álló sort, akkor csak a kilépési kódot ragaszthatom be:

{ foo; echo "$?"; } | awk "!/[^0-9]/ {exit($0)} {…}" 

Vagy ha tudom, hogy a foo kimenete soha nem tartalmaz csak .:

{ foo; echo .; echo "$?"; } | awk "/^\.$/ {getline; exit($0)} {…}" 

Ez mindig megtehető, ha van valamilyen módja a bar az utolsó sor kivételével, és az utolsó sort adja meg kilépési kódként.

Ha bar egy összetett folyamat, amelynek kimenete nem szükséges, megkerülheti annak egy részét, ha egy másik fájlleíróra nyomtatja a kilépési kódot.

exit_codes=$({ { foo; echo foo:"$?" >&3; } | { bar >/dev/null; echo bar:"$?" >&3; } } 3>&1) 

Ezt követően $exit_codes általában foo:X bar:Y, de bar:Y foo:X lehet, ha bar kilép mielőtt elolvassa az összes bemenetét, vagy ha nincs szerencséje. Úgy gondolom, hogy az 512 bájtig terjedő csövekre írások minden egyes egységen atomok, így a foo:$? és a bar:$? részeket nem lehet összekeverni, mivel mindaddig, amíg a tag karakterláncok 507 bájt alatt vannak.

Ha el kell készítenie a kimenetet a bar fájlból, az megnehezül. Kombinálhatja a fenti technikákat az mert a bar kimenete soha ne tartalmazzon egy sort, amely kinézetnek egy kilépési kód jelzése, de fiddly.

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") 

És természetesen van egy egyszerű lehetőség, amikor ideiglenes fájlt használ az állapot tárolásához. Egyszerű, de nem ilyen egyszerű a gyártás során:

  • Ha több szkript fut egyidejűleg, vagy ha ugyanaz a szkript több helyen használja ezt a módszert, akkor meg kell biztos, hogy különböző ideiglenes fájlneveket használnak.
  • Nehéz ideiglenes fájlt biztonságosan létrehozni egy megosztott könyvtárban. Gyakran a /tmp az egyetlen hely, ahol a szkript biztosan képes fájlokat írni. Használja a mktemp alkalmazást, amely nem POSIX, de manapság minden komolyabb egységen elérhető.
foo_ret_file=$(mktemp -t) { foo; echo "$?" >"$foo_ret_file"; } | bar bar_ret=$? foo_ret=$(cat "$foo_ret_file"; rm -f "$foo_ret_file") 

Megjegyzések

  • Az ideiglenes fájl megközelítés használatakor inkább egy csapdát adok az EXIT-hez, amely eltávolít minden ideiglenes fájlt így nem marad szemét akkor sem, ha a szkript meghal.

Válasz

A folyamatból indulva:

foo | bar | baz 

Íme egy általános megoldás, amely csak POSIX shell-t használ, ideiglenes fájlok nélkül:

exec 4>&1 error_statuses="`((foo || echo "0:$?" >&3) | (bar || echo "1:$?" >&3) | (baz || echo "2:$?" >&3)) 3>&1 >&4`" exec 4>&- 

A $error_statuses véletlenszerű sorrendben tartalmazza az esetleges sikertelen folyamatok állapotkódjait, indexekkel megadva, hogy melyik parancs adta ki az egyes állapotokat.

# 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 

Vegye figyelembe a tesztjeimben a $error_statuses körüli idézőjeleket; nélkülük grep nem képes megkülönböztetni, mert az új vonalak kényszerülnek a szóközökre.

Válasz

Ha telepítve van a moreutils csomag, használhatja a mispipe segédprogramot, amely pontosan azt teszi, amit kért.

Válasz

Tehát egy olyan válaszhoz akartam hozzájárulni, mint a lesmana, de szerintem az enyém talán valamivel egyszerűbb és valamivel előnyösebb tiszta- Bourne-shell megoldás:

# 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. 

Úgy gondolom, hogy ez belülről a legjobban magyarázható – a command1 végrehajtja és kinyomtatja a szokásos kimenetét az stdout fájlra (1. fájlleíró) , majd amint elkészült, a printf végrehajtja és kinyomtatja a command1 kilépési kódját az stdout-ján, de az stdout átirányításra kerül a 3. fájlleíróba.

Amíg a command1 fut, a stdout folyamatban van command2 (A printf kimenete soha nem jut el a command2-be, mert 1 helyett a 3 fájlleíróba küldjük, amit i s amit a pipa olvas).Ezután átirányítjuk a command2 kimenetét a 4 fájlleíróba, így az is kimarad az 1. fájlleíróból – mert azt szeretnénk, hogy az 1. fájlleíró egy kicsit később ingyen legyen, mert a 3. fájlleíró printf kimenetét visszahozzuk a 1. fájlleíró – mert ezt fogja elfogni a parancshelyettesítés (a backticks), és ez kerül a változóba.

A varázslat utolsó bitje az, hogy az első exec 4>&1 külön parancsként tettük meg – ez megnyitja a 4 fájlleírót a külső shell stdoutjának másolataként. A parancscsere helyettesíti a szabványra írtakat a benne lévő parancsok perspektívájából – de mivel a command2 kimenete a 4. fájlleíróba megy, ami a parancs helyettesítését illeti, a parancs helyettesítése nem rögzíti – ha azonban “kijön” a parancs helyettesítéséből, akkor továbbra is a szkript teljes fájlleírójához megy.

(A exec 4>&1 külön parancsnak lenni, mert sok közös héj nem kedveli, amikor megpróbál egy fájlleíróba írni egy parancs behelyettesítésén belül, amelyet a helyettesítést használó “külső” parancs nyit meg. Tehát ez a legegyszerűbb hordozható módszer )

Kevésbé technikásan és játékosabb módon nézheti meg, mintha a parancsok kimenetei egymást ugrálnák: a parancs1 átvezeti a 2. parancsba, majd a printf kimenete megugrik a 2. parancs felett, így a 2. parancs nem fogja el, majd a 2. parancs kimenete átugrik a th-ból A parancshelyettesítés éppen akkor, amikor a printf éppen időben landol, hogy elkapja a helyettesítés, hogy a változóba kerüljön, és a command2 kimenete tovább halad vidám módon, ahogyan a normál kimenetbe írja, mint egy normál csőben. / p>

Továbbá, ahogy megértem, a $? továbbra is tartalmazza a pip második parancsának visszatérési kódját, mert a változó hozzárendelések, a parancsok helyettesítései és az összetett parancsok mind hatékonyan átlátható a bennük levő parancs visszatérési kódja felé, ezért a 2. parancs visszatérési állapotát ki kell terjeszteni – ez, és mivel nem kell további funkciót meghatároznom, ezért gondolom, hogy ez valamivel jobb megoldás lehet, mint a javasolt írta: lesmana.

A lesmana megemlítései szerint lehetséges, hogy a command1 egy bizonyos ponton a 3. vagy 4. fájlleírót használja, így robusztusabbá téve:

exec 4>&1 exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1` exec 4>&- 

Ne feledje, hogy a példámban összetett parancsokat használok, de alhéjakat (a ( ) a { } helyett szintén működni fog, bár lehet, hogy kevésbé hatékony.)

A parancsok fájlleírókat örökölnek a folyamatból ami elindítja őket, így a teljes második sor örökölni fogja a négy fájlleírót, és az összetett parancs, amelyet a 3>&1 követ, örökölni fogja a három fájlleírót. Tehát a 4>&- gondoskodik arról, hogy a belső összetett parancs ne örökölje a négy fájlleírót, és a 3>&- nem örökölje a fájlleírót három, tehát A command1 “tisztább”, szabványosabb környezetet kap. A belső 4>&- t is áthelyezheti a 3>&- mellé, de kitalálom, miért ne korlátozhatnánk csak a hatókörét a lehető legnagyobb mértékben.

Nem vagyok biztos benne, hogy a dolgok milyen gyakran használják a harmadik és a negyedik fájlleírót – azt hiszem, a programok legtöbbször olyan rendszerhívásokat használnak, amelyek a pillanatban nem használt fájlleírókat adják vissza, de néha a kód fájlba ír Gondolom, közvetlenül a 3. leíró (el tudnám képzelni, hogy egy program ellenőrizze a fájlleírót, hogy meg van-e nyitva, és használja-e, ha van, vagy másként viselkedik, ha nem). Tehát ez utóbbi valószínűleg a legjobb szem előtt tartva, és általános célú esetekre használja.

Megjegyzések

  • Érdekesnek tűnik, de tudok ‘ Nem tudom pontosan kitalálni, hogy mit vár el ez a parancs, és a számítógépem sem tud ‘ t csinálni; kapok -bash: 3: Bad file descriptor.
  • @ G-Man, igaz, folyamatosan elfelejtem, hogy bash fogalmam sincs, mit csinál ‘ amikor együttműködik A fájlleírók a fájlok leírására szolgálnak, ellentétben az általam általában használt héjakkal (a busyboxhoz tartozó hamu). ‘ közölni fogom veled, amikor olyan megoldásra gondolok, amely boldoggá teszi a bash-t. Addig is, ha ‘ van egy debian boxod, akkor kipróbálhatod kötőjelben, vagy ha ‘ hasznos a busybox kipróbálhatja a bus / box ash / sh használatával.
  • @ G-Man Ami a parancs elvárásait illeti, és hogy mit csinál más héjakban, az a stdout átirányítása a1-es parancsról, tehát nem ‘ nem akad el a parancscserénél, de ha kívül esik a parancs behelyettesítésén, az fd3 visszaáll az stdout-ba, így ‘ s a várt módon továbbítja parancsolni2.A (z) 1 parancs kilépésekor a printf elindítja és kinyomtatja a kilépési állapotát, amelyet a váltás a parancs behelyettesítésével rögzít. Nagyon részletes lebontás itt: stackoverflow.com/questions/985876/tee-and-exit-status/… szintén , ez a kommented úgy hangzott, mintha kissé sértőnek szánta volna?
  • Hol kezdjem? (1) Sajnálom, ha sértettnek érezte magát. A „Úgy néz ki, érdekes” komolyan gondolták; nagyon jó lenne, ha olyan kompakt dolog működne, mint amire számítottál. Ezen túl egyszerűen azt mondtam, hogy nem értem, mit kellett volna tennie a megoldásoddal. hosszú óta dolgozom / játszom a Unixszal (a Linux létezése előtt), és ha valamit nem értek, ez egy piros zászló, ami talán mások sem fogják megérteni, és hogy további magyarázatra szorul (IMNSHO). … (Folytatás)
  • (folytatás) … Mivel te “… szeretsz gondolkodni … amit [te] csak értesz minden, ami több, mint az átlagembereknél ”, talán emlékeznie kell arra, hogy a Stack Exchange célja nem az, hogy parancsírási szolgáltatás legyen, és több ezer egyszeri megoldást bonyolítson le triviálisan különálló kérdésekre; hanem inkább megtanítsa az embereket arra, hogyan oldják meg saját problémáikat. Ehhez talán elég jól meg kell magyaráznia a dolgokat, hogy az „átlagember” megértse. Nézze meg a lesmana válaszát, és adjon példát egy kiváló magyarázatra. … (Folytatás)

Válasz

lesmana fenti megoldása szintén elvégezhető a a beágyazott alfolyamatokat a { .. } használatával indíthatja el (ne feledje, hogy a csoportosított parancsok ezen formájának mindig pontosvesszővel kell végződnie). Valami ilyesmi:

{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | stdintoexitstatus; } 4>&1 

Megvizsgáltam ezt a konstrukciót a kötőjel 0.5.5, valamint a bash 3.2.25 és 4.2.42 verzióival, tehát még akkor is, ha egyes héjak nem támogatják { .. } csoportosítás, továbbra is POSIX-kompatibilis.

Megjegyzések

  • Ez nagyon jól működik a legtöbb I ‘ ve kipróbálta, beleértve a NetBSD sh, pdksh, mksh, dash, bash elemeket. De ‘ nem tudom elérni, hogy az AT & T Ksh (93s +, 93u +) vagy zsh (4.3.9, 5.2), még set -o pipefail ksh-ben vagy tetszőleges számú szórt wait parancsot ad bármelyikben. Azt hiszem, részben lehet Legalább legyen a ksh elemzési kérdése, mintha ragaszkodnék az alhéjak használatához, akkor ez jól működik, de még if esetén is válaszd a ksh alhéj változatát, de hagyd az összetett parancsokat mások számára ez nem sikerül.

Válasz

A követés a @Patrik válaszának kiegészítőjeként értendő, abban az esetben, ha nem tudja használni a gyakori megoldások egyikét.

Ez a válasz a következőket feltételezi:

  • Van egy héja, amely nem ismeri a $PIPESTATUS sem set -o pipefail
  • Pipelt szeretne használni párhuzamos végrehajtáshoz, így nincsenek ideiglenes fájlok.
  • Ön ne akarjon további rendetlenséget okozni, ha megszakítja a szkriptet, esetleg hirtelen áramkimaradás miatt.
  • Ennek a megoldásnak viszonylag könnyen követhetőnek és tisztán olvashatónak kell lennie.
  • Megteszi nem akar további alhéjakat bevezetni.
  • Nem lehet a meglévő fájlleírókkal hegedülni, ezért a stdin / out / err-hez nem szabad hozzányúlni (hogyan átmenetileg bevezethet valami újat)

További feltételezések. Megszabadulhat mindettől, de ez túlságosan kirabolja a receptet, ezért itt nem foglalkozunk vele:

  • Annyit kell tudni, hogy a PIPE összes parancsának 0. kilépési kódja van.
  • Nincs szükség további oldalsáv-információkra.
  • A héja valóban megvárja az összes csőparancs visszatérését.

Korábban: foo | bar | baz, de ez csak az utolsó parancs kilépési kódját adja vissza (baz)

Wanted: $? nem lehet 0 (true), ha a csőben lévő parancsok bármelyike meghiúsult

Utána:

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 

Megmagyarázva:

  • Egy tempfájl létrehozása a következővel történik: mktemp. Ez általában azonnal létrehoz egy fájlt a /tmp
  • fájlban. Ez a tempfájl átirányításra kerül az FD 9-be írásra és az FD 8-ba olvasásra
  • Ezután a A tempfile azonnal törlődik. Bár nyitva marad, amíg mindkét FD megszűnik.
  • Most elindul a cső. Minden lépés csak az FD 9-hez ad hozzá, ha hiba történt.
  • A wait szükséges a ksh mezőhöz, mert a ksh else nem várja meg az összes csőparancs befejezését. Kérjük, vegye figyelembe, hogy vannak nem kívánt mellékhatások, ha vannak háttérfeladatok, ezért alapértelmezés szerint megjegyeztem.Ha a várakozás nem árt, akkor megjegyzést fűzhet hozzá.
  • Ezt követően a fájl tartalma beolvasásra kerül. Ha üres (mert minden működött) read visszaadja a false értéket, ezért a true hibát jelez

Ez plugin-helyettesítőként használható egyetlen parancs, és csak a következőkre van szüksége:

  • Nem használt 9. és 8. FD
  • Egyetlen környezeti változó a tempfájl nevének megtartására
  • És ez A recept szinte minden olyan héjhoz adaptálható, amely lehetővé teszi az IO átirányítását
  • Emellett meglehetősen platformos agnosztikus és nincs szüksége olyan dolgokra, mint /proc/fd/N

HIBÁK:

Ennek a szkriptnek hibája van, ha a /tmp fájl elfogy. Ha védelemre van szüksége ez ellen a mesterséges eset ellen is, megteheti a következőképpen, ennek azonban az a hátránya, hogy a 0 száma a 000 fájlban a parancsok számától függ.cső, így valamivel bonyolultabb:

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" 

Hordozhatósági megjegyzések:

  • ksh és hasonló héjakhoz, amelyek csak az utolsó pipe parancsra várnak, a wait nem kommentált

  • Az utolsó példa printf "%1s" "$?" a echo -n "$?" helyett, mert ez jobban hordozható. Nem minden platform értelmezi helyesen az -n szót.

  • printf "$?" is megtenné, printf "%1s" azonban elkap néhány sarok esetet, ha a szkriptet egy valóban meghibásodott platformon futtatja. (Olvassa el: ha véletlenül a paranoia_mode=extreme fájlba programoz.)

  • Az FD 8 és az FD 9 magasabb lehet azokon a platformokon, amelyek többszörösen támogatják számjegyeket. A POSIX-nak megfelelő héjnak csak az egyes számjegyeket kell támogatnia.

  • A Debian 8.2 verzióval tesztelték sh, bash, ksh, ash, sash és még csh

Válasz

Ez hordozható, azaz bármely POSIX-kompatibilis héjjal működik, nem szükséges, hogy az aktuális könyvtár írható legyen, és lehetővé teszi több, ugyanazon trükköt használó szkript egyidejű futtatását.

(foo;echo $?>/tmp/_$$)|(bar;exit $(cat /tmp/_$$;rm /tmp/_$$)) 

Szerkesztés: itt van egy erősebb verzió, amely a Gilles “megjegyzéseit követi:

(s=/tmp/.$$_$RANDOM;((foo;echo $?>$s)|(bar)); exit $(cat $s;rm $s)) 

Edit2: és itt van egy kissé könnyebb változat a kétes jim megjegyzés után:

(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s)) 

Megjegyzések

  • Ez több okból nem működik. 1. Az ideiglenes fájl ‘ írása előtt olvasható. 2. Nem biztonságos egy ideiglenes fájl létrehozása kiszámítható nevű megosztott könyvtárban (triviális DoS, symlink verseny). 3. Ha ugyanaz a szkript többször használja ezt a trükköt, akkor ‘ mindig ugyanazt a fájlnevet használja. Az 1 megoldásához olvassa el a fájlt a folyamat befejezése után. A 2. és 3. megoldáshoz használjon véletlenszerűen létrehozott nevű ideiglenes fájlt vagy egy privát könyvtárban.
  • +1 Nos, a $ {PIPESTATUS [0]} könnyebb, de az itt szereplő alapötlet működik, ha tudni kell a Gilles által említett problémákról.
  • Elmenthet néhány alhéjat: (s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s)). @Johan: Egyetértek azzal, hogy ‘ könnyebb a Bash-szel, de bizonyos összefüggésekben megéri tudni, hogyan lehet elkerülni a Bash-t.

Válasz

Kis elővigyázatossággal ennek működnie kell:

foo-status=$(mktemp -t) (foo; echo $? >$foo-status) | bar foo_status=$(cat $foo-status) 

Megjegyzések

  • Mi lenne a takarítással, mint a jlliagre? Ne ‘ ne hagyjon egy foo-status nevű fájlt?
  • @Johan: Ha inkább a javaslatomat szeretné, ne / div> t habozik megszavazni;) Amellett, hogy nem hagy el egy fájlt, az az előnye, hogy több folyamatnak lehetővé teszi egyidejű futtatását, és az aktuális könyvtárnak nem kell írhatónak lennie.

Válasz

A következő “if” blokk csak akkor fut, ha a “parancs” sikeres volt:

if command; then # ... fi 

Konkrétan, futtathat valami hasonlót:

haconf_out=/path/to/some/temporary/file if haconf -makerw > "$haconf_out" 2>&1; then grep -iq "Cluster already writable" "$haconf_out" # ... fi 

Melyik fog futni haconf -makerw és tárolja stdout-ját és stderr-jét a “$ haconf_out” könyvtárba. Ha a haconf visszaadott értéke igaz, akkor az “if” blokk futtatásra kerül, és a grep “$ haconf_out” feliratot fog illeszteni a “már írható fürt” -höz.

Vegye figyelembe, hogy a csövek automatikusan megtisztítják magukat; az átirányítással körültekintően kell eljárnia a (z) “$ haconf_out” eltávolításáért, ha kész.

Nem olyan elegáns, mint a pipefail, de egy jogos alternatíva, ha ez a funkció nincs elérhető közelségben.

Válasz

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 $ 

Válasz

(Legalább bash-szal) kombinálva a set -e -vel az alhéj használatával kifejezetten utánozhatja a pipefail-ot és kiléphet a pipe hibából

set -e foo | bar ( exit ${PIPESTATUS[0]} ) rest of program 

Tehát ha a foo valamilyen oknál fogva sikertelen – a program többi része nem kerül végrehajtásra, és a szkript kilép a megfelelő hibakóddal. (Ez azt feltételezi, hogy a foo kinyomtatja saját hibáját, ami elegendő a kudarc okának megértéséhez)

Válasz

SZERKESZTÉS : Ez a válasz helytelen, de érdekes, ezért a jövőre hagyom hivatkozás.


! hozzáadása a parancshoz megfordítja a visszatérési kódot.

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. # =========================================================== # 

Megjegyzések

  • Úgy gondolom, hogy ez nem függ össze. Példájában a ls kimeneti kódját szeretném megismerni, nem pedig a bogus_command
  • Azt javaslom, hogy vonja vissza ezt a választ.
  • Nos, úgy tűnik, én ‘ idióta vagyok. I ‘ ezt ténylegesen egy szkriptben használták, mielőtt azt gondoltam volna, hogy azt csinálta, amit az OP akart. Jó, hogy nem használtam ‘ semmire, ami fontos volt

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük