$> set -o pipefail
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли
اینجا fortune
برنامه شرطی بدون exit(rand())
.
می توانید توضیح دهید؟ اینجا چه اشکالی دارد?
انحراف غنایی-تاریخی
من اولین بار ربع قرن پیش با این هایزنباگ آشنا شدم. سپس برای Gateway در 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"
صادقانه بگویم، من بلافاصله "آشنای" قدیمی خود را نشناختم. علاوه بر این، این پروژه قبلاً بارها در Elbrus و تحت توزیعهای مختلف از جمله 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 بایت تا ≈64K، در اکثر سیستم عامل ها ≥4K). در غیر این صورت ممکن است مشکل برگردد.
چگونگی راه انداختن EPIPE
و سایر خطاهای ضبط؟
هیچ راه حل درستی وجود ندارد، اما توصیه های ساده ای وجود دارد:
EPIPE
مورد نیاز است باید پردازش شود (و در وضعیت خروج منعکس می شود) هنگام خروجی داده هایی که نیاز به یکپارچگی دارند. به عنوان مثال، در طول عملیات بایگانی یا ابزارهای پشتیبان.EPIPE
بهتر است نادیده گرفته شود هنگام نمایش اطلاعات و پیام های کمکی به عنوان مثال، هنگام نمایش اطلاعات گزینه ها--help
یا--version
.- اگر کد در حال توسعه می تواند قبلاً در خط لوله استفاده شود
| head
، و سپسEPIPE
بهتر است نادیده گرفته شود وگرنه بهتر است در وضعیت خروج پردازش و منعکس شود.
من از این فرصت استفاده می کنم و از تیم ها تشکر می کنم
همینطور ادامه بده رفقا، همینطور
سپاس ها
KDPV از
منبع: www.habr.com