$> set -o pipefail
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли
Täällä fortune
ehdollinen ohjelma ilman exit(rand())
.
Voitko selittää? mikä tässä on vialla?
Lyyris-historiallinen poikkeama
Tutustuin tähän Heisenbugiin ensimmäisen kerran neljännesvuosisata sitten. Sitten FaxNETin yhdyskäytävää varten oli tarpeen luoda useita apuohjelmia kautta
Aiempi kokemukseni sendmail- ja uucp/uupc-virheiden käsittelystä lisäsi huolellisuuttani " perusteellisessa virheenkäsittelyssä ". Ei ole mitään järkeä sukeltaa tuon tarinan yksityiskohtiin, mutta kamppailin tämän Heisenbugin kanssa kaksi viikkoa 10-14 tuntia. Siksi se muistettiin, ja eilen tämä vanha tuttava poikkesi käymään uudelleen.
TL;DR Vastaus
Apuohjelma head
voida sulje kanava alkaen fortune
сразу heti kun hän lukee ensimmäisen rivin. Jos fortune
tulostaa useamman kuin yhden rivin, sitten vastaavan puhelun write()
palauttaa virheilmoituksen tai raportoi, että vähemmän tavuja tulostetaan kuin pyydetään. Puolestaan kirjoitettu huolellisella virheenkäsittelyllä fortune
on oikeus esittää tämä tilanne poistumistilassaan. Siis asennuksen takia set -o pipefail
toimii || echo "Вы проиграли"
.
Kuitenkin, head
ei ehkä ehdi ajoissa sulje ennen fortune
lopettaa tietojen tulostamisen. Sitten se toimii && echo "Повезло!"
.
Yhdessä tämän päivän GNUMakefile
echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'
Käännetty ihmiseksi
Se on yleistä täällä --version
se kysyy, kuka hän on, ja jos vaihtoehtoa ei tueta, lisätään tynkä "Käytä GCC- tai CLANG-yhteensopivaa kääntäjää".
kuten
#define MDBX_BUILD_COMPILER "lcc:1.23.20:Sep--4-2019:e2k-v3-linux Please use GCC or CLANG compatible compiler"
Suoraan sanottuna en heti tunnistanut vanhaa "tuttavaani". Lisäksi projektia on testattu jo monta kertaa Elbruksella ja useilla eri jakeluilla, mukaan lukien Alt. Eri kääntäjillä, GNU Maken ja bashin versioilla. Siksi en halunnut nähdä virhettäni tässä.
Kun yritettiin toistaa ongelmaa ja/tai ymmärtää, mitä oli tekeillä, alkoi tapahtua enemmän outoja asioita.
Komentorivin loitsu:
echo "#define MDBX_BUILD_COMPILER '$(set -o pipefail; LC_ALL=C cc --version | head -1 || echo "Please use GCC or CLANG compatible compiler")'"
Välillä se tuotti ylimääräistä tekstiä, sitten ei... Usein jompikumpi vaihtoehdoista jäi kiinni melko pitkäksi aikaa, mutta jos nyökkäsi pidempään, niin sai aina molemmat!
Tietenkin, strace
Muuten, kuten kuka tahansa itseään kunnioittava strace
ei halua lisääntyä.
Joten mitä tapahtuu?
- Apuohjelma
head
on oikeus (tai pikemminkin jopa pakotettu) sulkea luettava kanava heti, kun se lukee pyydetyn määrän rivejä. - Tietoa luova ohjelman kirjoittaja (tässä tapauksessa
cc
) voida tulostaa useita rivejä ja vapaa tee tämä useiden puhelujen kauttawrite()
. - Jos lukijalla on aikaa sulkea kanava puoleltaan ennen kirjoittajan puolen tallennuksen päättymistä, jolloin kirjoittaja saa virheilmoituksen.
- Kirjoittaja ohjelma on oikeus molemmat jättävät huomioimatta kanavan kirjoitusvirheen ja heijastavat sen viimeistelykoodissasi.
- Asennuksen vuoksi
set -o pipefail
liukuhihnan valmistumiskoodi on nollasta poikkeava (virheellinen), jos tulos on muu kuin nolla vähintään yhdestä elementistä, ja sitten se toimii|| echo "Please use GCC or CLANG compatible compiler"
.
Saattaa olla vaihtelua riippuen siitä, kuinka kirjoitusohjelma toimii signaalien kanssa. Ohjelma voi esimerkiksi päättyä epänormaalisti (muun kuin nolla/virhe lopetustilan automaattisen luomisen yhteydessä) tai write()
palauttaa tuloksen, kun kirjoitetaan vähemmän tavuja kuin pyydettiin ja asetettu errno = EPIPE
.
Kuka syyttää?
Kuvatussa tapauksessa vähän kaikkea. Virhe käsittelyssä cc
(lcc:1.23.20:Sep—4-2019:e2k-v3-linux) ei ole tarpeeton. Monissa tapauksissa on parempi erehtyä varovaisuuteen, vaikka se paljastaa äkilliset puutteet perinteiseen käyttäytymiseen suunnitellussa kattilassa.
Mitä tehdä?
väärä:
fortune | head -1 && echo "Повезло, но вы рискуете!" || echo "WTF?"
korjaa:
-
(fortune && echo "Успешно" || echo "Ошибка") | head -1
Tässä komentotulkki käsittelee putken varhaisen sulkemisen huollettaessa sisäkkäistä liukuhihnaa (suluissa). Vastaavasti jos
fortune
ilmoittaa kirjoitusvirheestä suljetulle kanavalle tilassa ja sitten ulostuloon|| echo "Ошибка"
se ei pääse mihinkään, koska kanava on jo suljettu. -
fortune | cat - | head -1 && echo "Успешно" || echo "Ошибка"
Tässä on apuohjelma
cat
toimii vaimentimena, koska se jättää virheen huomioimattaEPIPE
vetäytymisen yhteydessä. Tämä riittää nyt johtopäätökseksifortune
pieni (useita rivejä) ja mahtuu kanavapuskuriin (512 tavusta ≈64K, useimmissa käyttöjärjestelmissä ≥4K). Muuten ongelma saattaa palata.
Kuinka käsitellä oikein EPIPE
ja muita tallennusvirheitä?
Ei ole yhtä oikeaa ratkaisua, mutta on olemassa yksinkertaisia suosituksia:
EPIPE
edellytetään täytyy käsitellä (ja näkyy poistumistilassa), kun tulostetaan tietoja, jotka edellyttävät eheyttä. Esimerkiksi arkistointi- tai varmuuskopiointiohjelmien käytön aikana.EPIPE
parempi jättää huomiotta tietoja ja apuviestejä näytettäessä. Esimerkiksi kun näytetään tietoja vaihtoehdoista--help
tai--version
.- Jos kehitettävää koodia voidaan käyttää liukuhihnassa ennen
| head
, SittenEPIPE
On parempi jättää huomiotta, muuten on parempi käsitellä ja heijastaa poistumistilaa.
Haluan käyttää tilaisuutta hyväkseni ja ilmaista kiitokseni joukkueille
Jatka samaan malliin Camarades, pysty
Kiitos
KDPV alkaen
Lähde: will.com