
$> set -o pipefail
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проигралиاینجا fortune برنامه شرطی بدون exit(rand()).
می توانید توضیح دهید؟ اینجا چه اشکالی دارد?
انحراف غنایی-تاریخی
من اولین بار ربع قرن پیش با این هایزنباگ آشنا شدم. سپس برای Gateway در FaxNET لازم بود چندین ابزار از طریق ایجاد شود "بازی چکرز" تحت FreeBSD. همانطور که انتظار می رفت، من خود را یک برنامه نویس پیشرفته و نسبتاً با تجربه می دانستم. از این رو قصد داشتم هر کاری را تا حد امکان با دقت و دقت انجام دهم و توجه ویژه ای به رسیدگی به خطا داشته باشم...
تجربه قبلی من از برخورد با اشکالات در sendmail و uucp/uupc به کوشش من در "کنترل کامل خطا" افزود. غواصی در جزئیات آن داستان فایده ای ندارد، اما من دو هفته با این هایزنباگ به مدت 10-14 ساعت مبارزه کردم. از این رو به یادگار ماند و دیروز این آشنای قدیمی برای دیدار دوباره رفت.
TL;DR پاسخ
سودمندی head می تواند بستن کانال از fortune сразу به محض خواندن خط اول. اگر fortune خروجی بیش از یک خط، سپس تماس مربوطه write() خطایی را برمیگرداند یا گزارش میدهد که تعداد بایتهای کمتری نسبت به درخواست شده خروجی میشود. به نوبه خود با مدیریت دقیق خطا نوشته شده است fortune حق دارد این وضعیت را در وضعیت خروج خود منعکس کند. سپس به دلیل نصب set -o pipefail کار خواهد کرد || echo "Вы проиграли".
با این حال، head ممکن است به موقع نتواند قبل از بستن fortune خروجی داده را به پایان خواهد رساند. سپس کار خواهد کرد && echo "Повезло!".
در یکی از امروز من چنین وجود دارد :
echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'به انسان ترجمه شده است
اینجا رایج است برای и به روش کامپایلر با استفاده از گزینه --version می پرسد که او کیست، و اگر گزینه پشتیبانی نمی شود، یک خرد درج می شود "لطفا از کامپایلر سازگار با GCC یا CLANG استفاده کنید".
مانند میتوانید آن را هر جایی پیدا کنید. مدتها پیش در همین مکان ظاهر شد و همه جا کاملاً خوب کار میکرد (Linux، سولاریس، OSX، FreeBSD، و غیره.). اما دیروز در بر روی پلت فرم من متوجه شدم:
#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، اما فرصتی برای فشار دادن 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?"به درستی:
(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
