$> set -o pipefail
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли
Tukaj fortune
pogojni program brez exit(rand())
.
Lahko razložiš? kaj je tukaj narobe?
Lirično-zgodovinski odmik
S tem Heisenbugom sem se prvič seznanil pred četrt stoletja. Nato je bilo za prehod v FaxNET potrebno ustvariti več pripomočkov via
Moje prejšnje izkušnje z odpravljanjem hroščev v sendmailu in uucp/uupc so prispevale k moji marljivosti pri "temeljitem obravnavanju napak." Nima smisla se potapljati v podrobnosti te zgodbe, vendar sem se s tem Heisenbugom boril dva tedna po 10-14 ur. Zato se je spomnil in včeraj se je ta stari znanec spet ustavil na obisku.
TL; DR Odgovor
Uporabnost head
lahko zaprite kanal iz fortune
сразу takoj ko prebere prvo vrstico. če fortune
izpiše več kot eno vrstico, nato ustrezen klic write()
bo vrnil napako ali sporočil, da je izhodnih manj bajtov, kot je zahtevano. V zameno, napisano s skrbnim obravnavanjem napak fortune
ima pravico odražati to situacijo v svojem izstopnem statusu. Potem zaradi namestitve set -o pipefail
bo delovalo || echo "Вы проиграли"
.
Vendar pa head
morda ne bo uspelo pravočasno zapreti pred fortune
bo končal z izpisom podatkov. Potem bo šlo && echo "Повезло!"
.
V enem od mojih današnjih GNUMakefile
echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'
Prevedeno v človeško
Tukaj je običajno za --version
vpraša, kdo je, in če možnost ni podprta, se vstavi škrbina "Prosimo, uporabite GCC ali CLANG združljiv prevajalnik".
kot
#define MDBX_BUILD_COMPILER "lcc:1.23.20:Sep--4-2019:e2k-v3-linux Please use GCC or CLANG compatible compiler"
Odkrito povedano, svojega starega "znanca" nisem takoj prepoznal. Poleg tega je bil projekt že večkrat preizkušen na Elbrusu in v številnih različnih distribucijah, vključno z Alt. Z različnimi prevajalniki, različicami GNU Make in bash. Zato tukaj nisem želel videti svoje napake.
Ko sem poskušal ponoviti težavo in/ali razumeti, kaj se dogaja, so se začele dogajati bolj čudne stvari.
Urok ukazne vrstice:
echo "#define MDBX_BUILD_COMPILER '$(set -o pipefail; LC_ALL=C cc --version | head -1 || echo "Please use GCC or CLANG compatible compiler")'"
Tu in tam je proizvedlo dodatno besedilo, nato pa ne ... Pogosto je ena od možnosti ostala precej dolgo, če pa ste dlje časa brskali, ste vedno dobili obe!
Seveda, strace
Mimogrede, kot vsak samospoštljiv strace
se raje ne razmnožuje.
Torej, kaj se dogaja?
- Uporabnost
head
ima pravico (ali bolje rečeno, je celo prisiljen), da zapre kanal, ki ga bere, takoj ko prebere zahtevano število vrstic. - Pisatelj programa za generiranje podatkov (v tem primeru
cc
) lahko natisnite več vrstic in prost storite to prek več klicevwrite()
. - če bralec bo imel čas, da zapre kanal na svoji strani pred koncem snemanja na pisateljevi strani, nato bo pisec prejel napako.
- Program Writer imajo pravico oba prezreta napako pri pisanju kanala in jo odražata v vaši kodi za dokončanje.
- Zaradi namestitve
set -o pipefail
koda dokončanja cevovoda bo različna od nič (napačna), če bo rezultat vsaj enega elementa različen od nič, in potem bo delovala|| echo "Please use GCC or CLANG compatible compiler"
.
Glede na to, kako program za zapisovanje deluje s signali, lahko obstajajo razlike. Na primer, program se lahko nenormalno prekine (s samodejnim ustvarjanjem neničelnega/napačnega statusa zaključka) ali write()
bo vrnil rezultat pisanja manj bajtov, kot je zahtevano in nastavljeno errno = EPIPE
.
Kdo je kriv?
V opisanem primeru vsega po malo. Obravnava napake v cc
(lcc:1.23.20:Sep—4-2019:e2k-v3-linux) ni odveč. V mnogih primerih je bolje biti previden, čeprav to razkrije nenadne napake v predlogi, zasnovani za tradicionalno vedenje.
Kaj storiti?
Napačno:
fortune | head -1 && echo "Повезло, но вы рискуете!" || echo "WTF?"
Pravilno:
-
(fortune && echo "Успешно" || echo "Ошибка") | head -1
Tukaj bo zgodnje zapiranje cevi obravnaval tolmač ukazov pri servisiranju ugnezdenega cevovoda (»znotraj« oklepajev). V skladu s tem, če
fortune
bo pisno sporočil napako zaprtemu kanalu v statusu, nato pa izhod|| echo "Ошибка"
nikamor ne pride, saj je kanal že zaprt. -
fortune | cat - | head -1 && echo "Успешно" || echo "Ошибка"
Tukaj je pripomoček
cat
deluje kot blažilnik, ker ignorira napakoEPIPE
ob umiku. Za zdaj je to dovolj za zaključekfortune
majhen (več vrstic) in se prilega medpomnilniku kanala (od 512 bajtov do ≈64K, v večini OS ≥4K). V nasprotnem primeru se lahko težava vrne.
Kako pravilno obdelati EPIPE
in druge napake pri snemanju?
Enotne prave rešitve ni, obstajajo pa preprosta priporočila:
EPIPE
obvezna je treba obdelati (in se odraža v statusu izhoda) pri izpisu podatkov, ki zahtevajo celovitost. Na primer med delovanjem arhivarjev ali pripomočkov za varnostno kopiranje.EPIPE
bolje ignorirati pri prikazovanju informacij in pomožnih sporočil. Na primer pri prikazu informacij o možnostih--help
ali--version
.- Če je kodo, ki se razvija, mogoče uporabiti v cevovodu pred
| head
, PotemEPIPE
Bolje je prezreti, sicer je bolje obdelati in odraziti v statusu izhoda.
Rad bi izkoristil to priložnost in se zahvalil ekipam
Kar tako naprej, Camarades, gor
Hvala
KDPV od
Vir: www.habr.com