$> set -o pipefail
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли
see on fortune
tingimuslik programm ilma exit(rand())
.
Kas saate selgitada? mis siin viga on?
Lüürilis-ajalooline kõrvalepõige
Esimest korda sain selle Heisenbugiga tuttavaks veerand sajandit tagasi. Seejärel tuli FaxNETi lüüsi jaoks luua mitu utiliiti
Minu varasem kogemus Sendmaili ja uucp/uupc vigadega tegelemisel suurendas minu hoolsust "põhjaliku veakäsitluse" alal. Selle loo detailidesse pole mõtet sukelduda, aga selle Heisenbugiga vaevlesin kaks nädalat 10-14 tundi. Seetõttu jäi see meelde ja eile astus see vana tuttav jälle külla.
TL;DR Vastus
Utiliit head
võib sulgege kanal alates fortune
korraga niipea, kui ta loeb esimest rida. Kui fortune
väljastab rohkem kui ühe rea, seejärel vastava kõne write()
tagastab veateate või teatab, et väljastatakse nõutust vähem baite. Omakorda hoolika veakäsitlusega kirjutatud fortune
on õigus seda olukorda oma väljumisolekus kajastada. Siis paigalduse tõttu set -o pipefail
töötab || echo "Вы проиграли"
.
Kuid head
ei pruugi õigeks ajaks jõuda enne kinni fortune
lõpetab andmete väljastamise. Siis see toimib && echo "Повезло!"
.
Ühes minu tänases päevas GNUMakefile
echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'
Inimeseks tõlgitud
See on siin tavaline --version
see küsib, kes ta on ja kui seda valikut ei toetata, siis sisestatakse tünn "Palun kasutage GCC või CLANG-iga ühilduvat kompilaatorit".
nagu
#define MDBX_BUILD_COMPILER "lcc:1.23.20:Sep--4-2019:e2k-v3-linux Please use GCC or CLANG compatible compiler"
Ausalt öeldes ei tundnud ma oma vana "tuttavat" kohe ära. Lisaks on projekti juba mitu korda testitud Elbrusel ja paljude erinevate distributsioonide all, sealhulgas Alt. Erinevate kompilaatorite, GNU Make ja bashi versioonidega. Seetõttu ei tahtnud ma siin oma viga näha.
Püüdes probleemi reprodutseerida ja/või aru saada, mis toimub, hakkas juhtuma kummalisemaid asju.
Käsurea loits:
echo "#define MDBX_BUILD_COMPILER '$(set -o pipefail; LC_ALL=C cc --version | head -1 || echo "Please use GCC or CLANG compatible compiler")'"
Aeg-ajalt toodaks lisateksti, siis mitte... Tihti jäi üks variant päris pikaks ajaks kinni, aga pikemalt torkides sai alati mõlemad!
Muidugi, strace
Muide, nagu iga endast lugupidav strace
eelistab mitte paljuneda.
Mis siis toimub?
- Utiliit
head
on õigus (õigemini isegi sunnitud) sulgeda loetav kanal kohe, kui see loeb nõutud arvu ridu. - Andmeid genereeriv programmikirjutaja (antud juhul
cc
) võib printida mitu rida ja tasuta tehke seda mitme kõne kauduwrite()
. - kui lugejal on aega enne kirjutajapoolse salvestuse lõppu kanal enda poolelt sulgeda, siis saab kirjutaja veateate.
- Kirjaniku programm õigus mõlemad ignoreerivad kanali kirjutamisviga ja kajastavad seda teie lõpetamiskoodis.
- Paigaldamise tõttu
set -o pipefail
konveieri lõpu kood on nullist erinev (vigane), kui tulemus on nullist erinev vähemalt ühest elemendist ja siis see töötab|| echo "Please use GCC or CLANG compatible compiler"
.
Sõltuvalt sellest, kuidas kirjutamisprogramm signaalidega töötab, võib esineda erinevusi. Näiteks võib programm ebanormaalselt lõppeda (null-/viga-lõpetamisoleku automaatse genereerimisega) või write()
tagastab tulemuse, kui kirjutatakse vähem baite, kui nõutud ja seatud errno = EPIPE
.
Kes on süüdi?
Kirjeldatud juhul natuke kõike. Viga sissetöötamisel cc
(lcc:1.23.20:Sep—4-2019:e2k-v3-linux) ei ole üleliigne. Paljudel juhtudel on parem eksida ettevaatusega, kuigi see paljastab tavapärase käitumise jaoks loodud katlaplaadi äkilised vead.
Mida teha?
Vale:
fortune | head -1 && echo "Повезло, но вы рискуете!" || echo "WTF?"
Õigesti:
-
(fortune && echo "Успешно" || echo "Ошибка") | head -1
Siin käsitleb toru varajast sulgemist käskude tõlk pesastatud konveieri teenindamisel (sulgude sees). Vastavalt sellele, kui
fortune
teatab kirjutamisveast olekus suletud kanalile, seejärel väljundile|| echo "Ошибка"
see ei jõua kuhugi, kuna kanal on juba suletud. -
fortune | cat - | head -1 && echo "Успешно" || echo "Ошибка"
Siin on utiliit
cat
toimib siibrina, kuna eirab vigaEPIPE
taganemisel. Sellest piisab praeguseks järelduseksfortune
väike (mitu rida) ja mahub kanalipuhvrisse (512 baidist ≈64K, enamikus OS-ides ≥4K). Vastasel juhul võib probleem taastuda.
Kuidas õigesti töödelda EPIPE
ja muid salvestusvigu?
Pole olemas ühte õiget lahendust, kuid on lihtsad soovitused:
EPIPE
nõutav tuleb töödelda (ja kajastub väljumisolekus) terviklikkust nõudvate andmete väljastamisel. Näiteks arhiivide või varundusutiliitide töötamise ajal.EPIPE
parem ignoreerida teabe ja abiteadete kuvamisel. Näiteks valikute kohta teabe kuvamisel--help
või--version
.- Kui arendatavat koodi saab enne konveier kasutada
| head
siisEPIPE
Parem on ignoreerida, vastasel juhul on parem töödelda ja kajastada väljumisolekut.
Kasutan võimalust ja tänan meeskondi
Jätkake, Camarad, jätka
Tänan
KDPV alates
Allikas: www.habr.com