Тағы бір Гейзенбуг қолтырауынның жанынан өтті

Тағы бір Гейзенбуг қолтырауынның жанынан өтті

$> set -o pipefail

$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!

$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли

Бұл fortune шартты бағдарлама жоқ exit(rand()).

Сіз түсіндіре аласыз ба? мұнда не болды?

Лирикалық-тарихи шегініс

Мен бұл Гейзенбугпен алғаш рет ширек ғасыр бұрын таныстым. Содан кейін FaxNET шлюзі үшін бірнеше утилиталар арқылы жасау қажет болды құбырлар FreeBSD астында «дойбы ойнау». Күткендей, мен өзімді озық және жеткілікті тәжірибелі бағдарламашы санадым. Сондықтан мен қателерді өңдеуге ерекше назар аудара отырып, барлығын мүмкіндігінше мұқият және мұқият істеуді мақсат еттім...

Менің 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')"'

Адам тіліне аударылған

Бұл жерде әдеттегідей GNU жасау и bash опциясын пайдаланып компиляторлық жолмен --version ол оның кім екенін сұрайды және опцияға қолдау көрсетілмесе, онда штрих енгізіледі «GCC немесе CLANG үйлесімді компиляторды пайдаланыңыз».

сияқты қазандық кез келген жерден табуға болады. Ол осы жерде бұрыннан пайда болды және барлық жерде тамаша жұмыс істеді (Linux, Solaris, OSX, FreeBSD, WSL және т.б.). Бірақ кеше altlinux платформасында Эльбрус 2000 (E2K) мен байқадым:

#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 біздің бәріміз! Осылайша, страс-тирадты теріп, бірақ Enter пернесін басып үлгермей, мен ескі досым Гейзенбуг мырзаны және әзірлеушілерді таныдым. құрастырушы Мен 25 жыл бұрын, Сағыныш… Ал мен мұңайып мына жазбаны жазуды жөн көрдім😉

Айтпақшы, кез келген өзін-өзі құрметтеу сияқты Гейзенбуг, астында 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?"

Дұрыс:

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

    Мұнда құбырды ерте жабуды кірістірілген құбырға қызмет көрсету кезінде («жақшаның ішінде») командалық интерпретатор өңдейді. Сәйкесінше, егер fortune күйдегі жабық арнаға жазбаша қате туралы хабарлайды, содан кейін шығыс || echo "Ошибка" ол ешқайда алмайды, өйткені арна жабылған.

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

    Бұл қызметтік бағдарлама cat бөгет ретінде әрекет етеді, себебі ол қатені елемейді EPIPE шығарылған кезде. Қорытынды жасау үшін бұл жеткілікті fortune шағын (бірнеше жол) және арна буферіне сәйкес келеді (512 байттан ≈64К дейін, көптеген ОЖ ≥4K). Әйтпесе, мәселе қайта оралуы мүмкін.

Қалай дұрыс өңдеу керек EPIPE және басқа жазу қателері?

Бірыңғай дұрыс шешім жоқ, бірақ қарапайым ұсыныстар бар:

  • EPIPE талап етіледі өңдеу керек (және шығу күйінде көрсетіледі) тұтастықты талап ететін деректерді шығару кезінде. Мысалы, архиваторлардың немесе резервтік утилиталардың жұмысы кезінде.
  • EPIPE елемеу жақсы ақпаратты және көмекші хабарламаларды көрсету кезінде. Мысалы, опциялар туралы ақпаратты көрсету кезінде --help немесе --version.
  • Егер әзірленіп жатқан код бұрын құбырда пайдаланылуы мүмкін болса | headсодан кейін EPIPE Елемеу жақсы, әйтпесе шығу күйінде өңдеу және көрсету жақсы.

Осы мүмкіндікті пайдаланып, командаларға алғысымды білдіргім келеді MCST и altlinux үлкен өнімді жұмыс үшін. Сіздің шешіміңіз керемет!
Осыны жалғастырыңыз Камарадес, жоғары күзгі кездесулер!

сізге рахмет берез қателер мен қателерді түзету үшін.
KDPV бастап Георгий А.

Ақпарат көзі: www.habr.com

пікір қалдыру