Še en Heisenbug mimo krokodila

Še en Heisenbug mimo krokodila

$> 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 cevi "igranje dame" pod FreeBSD. Po pričakovanjih sem se imel za naprednega in dokaj izkušenega programerja. Zato sem nameraval vse narediti čim bolj skrbno in skrbno, pri čemer sem posebno pozornost namenil odpravljanju napak ...

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 obstaja taka Drobec:

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 Ustvari GNU и bash na način prevajalnika z uporabo možnosti --version vpraša, kdo je, in če možnost ni podprta, se vstavi škrbina "Prosimo, uporabite GCC ali CLANG združljiv prevajalnik".

kot kurilna plošča je mogoče najti kjerkoli. Na tem mestu se je pojavil že dolgo nazaj in je povsod deloval brezhibno (Linux, Solaris, OSX, FreeBSD, WSL itd.). Toda včeraj v altlinux na platformi Elbrus 2000 (E2K) Opazil sem:

#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 naše vse! In ko sem vtipkal strace tirado, a nisem imel časa pritisniti Enter, sem prepoznal svojega starega prijatelja gospoda Heisenbuga in razvijalce prevajalnik sam pred 25 leti, Nostalgija… In odločila sem se, da bom žalostna in napišem ta zapis 😉

Mimogrede, kot vsak samospoštljiv Heisenbug, Spodaj 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č klicev write().
  • č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:

  1. (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.

  2. fortune | cat - | head -1 && echo "Успешно" || echo "Ошибка"

    Tukaj je pripomoček cat deluje kot blažilnik, ker ignorira napako EPIPE ob umiku. Za zdaj je to dovolj za zaključek fortune 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, Potem EPIPE Bolje je prezreti, sicer je bolje obdelati in odraziti v statusu izhoda.

Rad bi izkoristil to priložnost in se zahvalil ekipam MCST и altlinux za odlično produktivno delo. Vaša odločnost je neverjetna!
Kar tako naprej, Camarades, gor srečanja v jeseni!

Hvala brez za popravljanje tipkarskih napak in napak.
KDPV od Georgij A.

Vir: www.habr.com

Dodaj komentar