$> 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
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
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 --version
u kimligini so'raydi va agar variant qo'llab-quvvatlanmasa, u holda stub qo'yiladi "Iltimos, GCC yoki CLANG mos kompilyatoridan foydalaning".
Kabi
#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
Aytgancha, har qanday o'zini hurmat qilish kabi 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 qilingwrite()
. - 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:
-
(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. -
fortune | cat - | head -1 && echo "Π£ΡΠΏΠ΅ΡΠ½ΠΎ" || echo "ΠΡΠΈΠ±ΠΊΠ°"
Mana yordam dasturi
cat
damper vazifasini bajaradi, chunki u xatoga e'tibor bermaydiEPIPE
olib qo'yilganda. Hozir xulosa qilish uchun bu etarlifortune
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
| head
so'ngEPIPE
E'tibor bermaslik yaxshiroqdir, aks holda chiqish holatida ishlov berish va aks ettirish yaxshiroqdir.
Fursatdan foydalanib, jamoalarga o'z minnatdorchiligimni bildiraman
Davom eting Camarades, yuqoriga
Rahmat
dan KDPV
Manba: www.habr.com