Yana bir Geyzenbug timsoh yonidan o'tib ketdi

Yana bir Geyzenbug timsoh yonidan o'tib ketdi

$> set -o pipefail

$> fortune | head -1 > /dev/null && echo "ПовСзло!" || echo "Π’Ρ‹ ΠΏΡ€ΠΎΠΈΠ³Ρ€Π°Π»ΠΈ"
ПовСзло!

$> fortune | head -1 > /dev/null && echo "ПовСзло!" || echo "Π’Ρ‹ ΠΏΡ€ΠΎΠΈΠ³Ρ€Π°Π»ΠΈ"
Π’Ρ‹ ΠΏΡ€ΠΎΠΈΠ³Ρ€Π°Π»ΠΈ

u fortune shartli dastur holda exit(rand()).

Tushuntira olasizmi? bu erda nima bo'ldi?

Lirik-tarixiy chekinish

Men bu Geyzenbug bilan birinchi marta chorak asr oldin tanishganman. Keyin FaxNET-dagi shlyuz uchun bir nechta yordamchi dasturlarni yaratish kerak edi Quvurlar FreeBSD ostida "shashka o'ynash". Kutilganidek, men o'zimni ilg'or va juda tajribali dasturchi deb bildim. Shuning uchun men hamma narsani iloji boricha ehtiyotkorlik bilan va ehtiyotkorlik bilan bajarishni niyat qildim, xatolarni hal qilishga alohida e'tibor qaratdim ...

Sendmail va uucp/uupc-dagi xatolar bilan ishlash bo'yicha oldingi tajribam "xatolarni sinchkovlik bilan hal qilish" dagi tirishqoqligimga qo'shildi. Bu hikoyaning tafsilotlariga sho'ng'ishning ma'nosi yo'q, lekin men bu Heisenbug bilan ikki hafta davomida 10-14 soat davomida kurashdim. Shuning uchun eslab qoldi va kecha bu eski tanish yana tashrif buyurish uchun to'xtadi.

TL; DR javobi

Qulaylik head Jon kanalni yoping fortune сразу birinchi satrni o'qishi bilanoq. Agar fortune bir nechta satrni, keyin tegishli qo'ng'iroqni chiqaradi write() xatoni qaytaradi yoki so'ralgandan kamroq bayt chiqishi haqida xabar beradi. O'z navbatida, ehtiyotkorlik bilan xatolik bilan yozilgan fortune ushbu holatni o'zining chiqish holatida aks ettirish huquqiga ega. Keyin o'rnatish tufayli set -o pipefail ishlaydi || echo "Π’Ρ‹ ΠΏΡ€ΠΎΠΈΠ³Ρ€Π°Π»ΠΈ".

Biroq, head vaqtida bajara olmasligi mumkin oldin yopish fortune ma'lumotlarni chiqarishni tugatadi. Keyin u ishlaydi && echo "ПовСзло!".

Bugungi kunlarimdan birida GNUMakefile shunday bor parcha:

echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'

Inson tiliga tarjima qilingan

Bu erda keng tarqalgan GNU Make ΠΈ bosh opsiyasi yordamida kompilyator usulida --version u kimligini so'raydi va agar variant qo'llab-quvvatlanmasa, u holda stub qo'yiladi "Iltimos, GCC yoki CLANG mos kompilyatoridan foydalaning".

Kabi qozon istalgan joyda topish mumkin. U bu joyda uzoq vaqt oldin paydo bo'lgan va hamma joyda mukammal ishlagan (Linux, Solaris, OSX, FreeBSD, WSL va hokazo.). Ammo kecha altlinux platformada Elbrus 2000 (E2K) Men payqadim:

#define MDBX_BUILD_COMPILER "lcc:1.23.20:Sep--4-2019:e2k-v3-linux Please use GCC or CLANG compatible compiler"

Ochig'i, men eski "tanishimni" darhol tanimadim. Bundan tashqari, loyiha allaqachon Elbrusda ko'p marta sinovdan o'tgan va turli xil tarqatishlar, shu jumladan Alt. Turli kompilyatorlar, GNU Make va bash versiyalari bilan. Shuning uchun men bu erda xatoimni ko'rishni xohlamadim.

Muammoni takrorlashga va/yoki nima bo'layotganini tushunishga harakat qilganda, yana g'alati narsalar sodir bo'la boshladi.
Buyruqlar qatori imlosi:

echo "#define MDBX_BUILD_COMPILER '$(set -o pipefail; LC_ALL=C cc --version | head -1 || echo "Please use GCC or CLANG compatible compiler")'"

Vaqti-vaqti bilan u qo'shimcha matn ishlab chiqaradi, keyin esa yo'q ... Ko'pincha variantlardan biri ancha vaqt davomida qolib ketadi, lekin agar siz uzoqroq tursangiz, har doim ikkalasini ham olasiz!

Albatta, strace bizning hamma narsamiz! Va strace tiradni yozib, lekin Enter tugmasini bosishga ulgurmay, eski do'stim janob Heisenbug va ishlab chiquvchilarni tanidim. kompilyator 25 yil oldin o'zim Nostalji… Va men xafa bo'lishga va ushbu eslatmani yozishga qaror qildim πŸ˜‰

Aytgancha, har qanday o'zini hurmat qilish kabi Heisenbug, ostida strace ko'paytirmaslikni afzal ko'radi.

Xo'sh, nima bo'lyapti?

  • Qulaylik head o'qilayotgan kanal so'ralgan qatorlarni o'qishi bilanoq uni yopish huquqiga ega (aniqrog'i, hatto majbur).
  • Ma'lumotlarni ishlab chiqaruvchi dastur muallifi (bu holda cc) Jon bir nechta satrlarni chop eting va ozod buni bir nechta qo'ng'iroqlar orqali qiling write().
  • agar o'quvchi yozuvchi tomonida yozish tugagunga qadar o'z tomonidagi kanalni yopishga ulguradi, keyin yozuvchi xatoga yo'l qo'yadi.
  • Yozuvchi dasturi haqli ikkalasi ham kanal yozish xatosiga e'tibor bermaydi va uni yakuniy kodingizda aks ettiradi.
  • O'rnatish tufayli set -o pipefail Agar natija kamida bitta elementdan nolga teng bo'lsa, quvur liniyasini tugatish kodi nolga teng bo'lmagan (xato) bo'ladi va u ishlaydi || echo "Please use GCC or CLANG compatible compiler".

Yozuvchi dasturining signallar bilan qanday ishlashiga qarab farqlar bo'lishi mumkin. Masalan, dastur g'ayritabiiy tarzda tugatilishi mumkin (avtomatik ravishda nolga teng bo'lmagan/xato tugatish holatini yaratish bilan) yoki write() so'ralgan va o'rnatilgandan kamroq bayt yozish natijasini qaytaradi errno = EPIPE.

Kim aybdor?

Ta'riflangan holatda hamma narsadan bir oz. Ishlov berishda xatolik yuz berdi cc (lcc:1.23.20:Sepβ€”4-2019:e2k-v3-linux) emas ortiqcha. Ko'p hollarda ehtiyotkorlik bilan xato qilish yaxshiroqdir, garchi bu an'anaviy xatti-harakatlar uchun mo'ljallangan qozondagi to'satdan kamchiliklarni ochib beradi.

Nima qilish kerak?

Noto'g'ri:

fortune | head -1 && echo "ПовСзло, Π½ΠΎ Π²Ρ‹ рискуСтС!" || echo "WTF?"

To'g'ri:

  1. (fortune && echo "УспСшно" || echo "Ошибка") | head -1

    Bu erda quvurning erta yopilishi ichki quvur liniyasiga (qavslar ichida) xizmat ko'rsatishda buyruq tarjimoni tomonidan amalga oshiriladi. Shunga ko'ra, agar fortune holatidagi yopiq kanalga yozma xatolik haqida xabar beradi, keyin esa chiqish || echo "Ошибка" u hech qaerga bormaydi, chunki kanal allaqachon yopilgan.

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

    Mana yordam dasturi cat damper vazifasini bajaradi, chunki u xatoga e'tibor bermaydi EPIPE olib qo'yilganda. Hozir xulosa qilish uchun bu etarli fortune kichik (bir nechta satr) va kanal buferiga mos keladi (512 baytdan β‰ˆ64K gacha, aksariyat OS β‰₯4K). Aks holda muammo qaytib kelishi mumkin.

Qanday qilib to'g'ri ishlov berish kerak EPIPE va boshqa yozish xatolar?

Bitta to'g'ri yechim yo'q, lekin oddiy tavsiyalar mavjud:

  • EPIPE kerak qayta ishlanishi kerak yaxlitlikni talab qiladigan ma'lumotlarni chiqarishda (va chiqish holatida aks ettirilgan). Misol uchun, arxivatorlar yoki zaxira yordamchi dasturlarning ishlashi paytida.
  • EPIPE e'tibor bermaslik yaxshiroqdir ma'lumot va yordamchi xabarlarni ko'rsatishda. Masalan, variantlar haqida ma'lumotni ko'rsatishda --help yoki --version.
  • Agar ishlab chiqilayotgan kod ilgari quvur liniyasida ishlatilishi mumkin bo'lsa | headso'ng EPIPE E'tibor bermaslik yaxshiroqdir, aks holda chiqish holatida ishlov berish va aks ettirish yaxshiroqdir.

Fursatdan foydalanib, jamoalarga o'z minnatdorchiligimni bildiraman MCST ΠΈ altlinux katta samarali mehnat uchun. Sizning qat'iyatingiz ajoyib!
Davom eting Camarades, yuqoriga kuzda uchrashuvlar!

Rahmat berez matn terish va xatolarni tuzatish uchun.
dan KDPV Georgiy A.

Manba: www.habr.com

a Izoh qo'shish