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
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 apipefail
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é:
- 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.
- Létrejön egy cső, és a bal oldali parancsok (
#part3
) és a right (#part2
) végrehajtásra kerül. Azexit $xs
egyúttal a pip utolsó parancsa, ami azt jelenti, hogy az stdin karakterlánc az egész konstrukció kilépési állapota lesz. - 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. - A létrehozza a pipát, és a bal oldali (
#part5
és#part6
) és jobb (filter >&4
) végrehajtják. Azfilter
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 azfilter
kimenete a teljes konstrukció stdout-ja. - 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. -
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 azfilter
címre. Azfilter
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ödiksomeprog 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 abash 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
-o
pipefail
/tmp/ff
–getopt
(vagy hasonló) elemzés aoptarg
segítségével, amely a következő elem aARGV
, a-o
argumentumaké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 amktemp
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órtwait
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égif
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
semset -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 aksh
mezőhöz, mert aksh
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 afalse
értéket, ezért atrue
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, await
nem kommentált -
Az utolsó példa
printf "%1s" "$?"
aecho -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 aparanoia_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égcsh
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 abogus_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
pipestatus
a zsh-ben. Sajnos más héjak nem ‘ nem rendelkeznek ezzel a funkcióval.echo "$pipestatus[1]" "$pipestatus[2]"
.if [ `echo "${PIPESTATUS[@]}" | tr -s ' ' + | bc` -ne 0 ]; then echo FAIL; fi