$> set -o pipefail
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли
Бұл fortune
шартты бағдарлама жоқ exit(rand())
.
Сіз түсіндіре аласыз ба? мұнда не болды?
Лирикалық-тарихи шегініс
Мен бұл Гейзенбугпен алғаш рет ширек ғасыр бұрын таныстым. Содан кейін FaxNET шлюзі үшін бірнеше утилиталар арқылы жасау қажет болды
Менің sendmail және uucp/uupc қателерімен жұмыс істеудегі бұрынғы тәжірибем «қателерді мұқият өңдеуге» деген ынталылығыма қосылды. Бұл оқиғаның егжей-тегжейіне сүңгудің қажеті жоқ, бірақ мен бұл Гейзенбугпен екі апта бойы 10-14 сағат бойы күрестім. Сондықтан есте қалды, кеше осы ескі танысым тағы да қонаққа келіп тоқтады.
TL; DR Жауабы
Утилита head
болады арнаны жабыңыз fortune
сразу ол бірінші жолды оқыған бойда. Егер fortune
бірден көп жолды, содан кейін сәйкес қоңырауды шығарады write()
қатені қайтарады немесе сұралғандан аз байт шығарылатыны туралы есеп береді. Өз кезегінде, қателерді мұқият өңдеумен жазылған fortune
бұл жағдайды өзінің шығу мәртебесінде көрсетуге құқылы. Содан кейін орнатуға байланысты set -o pipefail
жұмыс істейтін болады || echo "Вы проиграли"
.
Алайда, head
уақытында үлгермеуі мүмкін бұрын жабыңыз fortune
деректерді шығаруды аяқтайды. Сонда ол жұмыс істейді && echo "Повезло!"
.
Бүгінгі күндерімнің бірінде GNUMakefile
echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'
Адам тіліне аударылған
Бұл жерде әдеттегідей --version
ол оның кім екенін сұрайды және опцияға қолдау көрсетілмесе, онда штрих енгізіледі «GCC немесе CLANG үйлесімді компиляторды пайдаланыңыз».
сияқты
#define MDBX_BUILD_COMPILER "lcc:1.23.20:Sep--4-2019:e2k-v3-linux Please use GCC or CLANG compatible compiler"
Шынымды айтсам, мен бұрынғы «танысымды» бірден танымадым. Сонымен қатар, жоба Эльбруста және көптеген әртүрлі дистрибутивтерде, соның ішінде Altта да бірнеше рет сынақтан өтті. Әртүрлі компиляторлармен, GNU Make және bash нұсқаларымен. Сондықтан мен бұл жерде қателігімді көргім келмеді.
Мәселені қайта шығаруға және/немесе не болып жатқанын түсінуге тырысқанда, оғаш нәрселер бола бастады.
Пәрмен жолы емлесі:
echo "#define MDBX_BUILD_COMPILER '$(set -o pipefail; LC_ALL=C cc --version | head -1 || echo "Please use GCC or CLANG compatible compiler")'"
Ара-тұра ол қосымша мәтін шығарады, содан кейін олай емес... Көбінесе опциялардың бірі ұзақ уақыт сақталады, бірақ егер сіз ұзағырақ тырнасаңыз, сіз әрқашан екеуін де аласыз!
Әрине, strace
Айтпақшы, кез келген өзін-өзі құрметтеу сияқты strace
көбейтпеуді жөн көреді.
Сонымен, не болып жатыр?
- Утилита
head
сұралған жолдар санын оқыған бойда оқылатын арнаны жабуға құқығы бар (дәлірек айтсақ, мәжбүрлі). - Деректерді жасаушы бағдарлама авторы (бұл жағдайда
cc
) болады бірнеше жолды басып шығару және Тегін мұны бірнеше қоңыраулар арқылы жасаңызwrite()
. - егер оқырман жазушы жағындағы жазба аяқталмай тұрып өз жағындағы арнаны жауып үлгереді, содан кейін жазушы қате алады.
- Жазушы бағдарламасы құқығы бар екеуі де арна жазу қатесін елемейді және оны аяқтау кодында көрсетеді.
- Орнатуға байланысты
set -o pipefail
құбырды аяқтау коды нөлге тең емес (қате) болады, егер нәтиже кем дегенде бір элементтен нөлге тең емес болса, содан кейін ол жұмыс істейді|| echo "Please use GCC or CLANG compatible compiler"
.
Жазушы бағдарламасының сигналдармен қалай жұмыс істейтініне байланысты өзгерістер болуы мүмкін. Мысалы, бағдарлама әдеттен тыс аяқталуы мүмкін (нөлдік емес/қателік тоқтату күйін автоматты түрде жасау арқылы) немесе write()
сұралған және орнатылғаннан азырақ байт жазу нәтижесін қайтарады errno = EPIPE
.
Кім кінәлі?
Сипатталған жағдайда бәрінен аз. Өңдеу қатесі cc
(lcc:1.23.20:Sep—4-2019:e2k-v3-linux) жоқ артық. Көптеген жағдайларда сақтықпен қателескен дұрыс, дегенмен бұл дәстүрлі мінез-құлыққа арналған қазандықтағы кенеттен кемшіліктерді көрсетеді.
Не істеу?
Қате:
fortune | head -1 && echo "Повезло, но вы рискуете!" || echo "WTF?"
Дұрыс:
-
(fortune && echo "Успешно" || echo "Ошибка") | head -1
Мұнда құбырды ерте жабуды кірістірілген құбырға қызмет көрсету кезінде («жақшаның ішінде») командалық интерпретатор өңдейді. Сәйкесінше, егер
fortune
күйдегі жабық арнаға жазбаша қате туралы хабарлайды, содан кейін шығыс|| echo "Ошибка"
ол ешқайда алмайды, өйткені арна жабылған. -
fortune | cat - | head -1 && echo "Успешно" || echo "Ошибка"
Бұл қызметтік бағдарлама
cat
бөгет ретінде әрекет етеді, себебі ол қатені елемейдіEPIPE
шығарылған кезде. Қорытынды жасау үшін бұл жеткіліктіfortune
шағын (бірнеше жол) және арна буферіне сәйкес келеді (512 байттан ≈64К дейін, көптеген ОЖ ≥4K). Әйтпесе, мәселе қайта оралуы мүмкін.
Қалай дұрыс өңдеу керек EPIPE
және басқа жазу қателері?
Бірыңғай дұрыс шешім жоқ, бірақ қарапайым ұсыныстар бар:
EPIPE
талап етіледі өңдеу керек (және шығу күйінде көрсетіледі) тұтастықты талап ететін деректерді шығару кезінде. Мысалы, архиваторлардың немесе резервтік утилиталардың жұмысы кезінде.EPIPE
елемеу жақсы ақпаратты және көмекші хабарламаларды көрсету кезінде. Мысалы, опциялар туралы ақпаратты көрсету кезінде--help
немесе--version
.- Егер әзірленіп жатқан код бұрын құбырда пайдаланылуы мүмкін болса
| head
содан кейінEPIPE
Елемеу жақсы, әйтпесе шығу күйінде өңдеу және көрсету жақсы.
Осы мүмкіндікті пайдаланып, командаларға алғысымды білдіргім келеді
Осыны жалғастырыңыз Камарадес, жоғары
сізге рахмет
KDPV бастап
Ақпарат көзі: www.habr.com