$> set -o pipefail
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли
ومن fortune
برنامج مشروط بدون exit(rand())
.
هل يمكن ان توضح؟ ما هو الخطأ هنا?
الاستطراد الغنائي التاريخي
لقد تعرفت على Heisenbug لأول مرة منذ ربع قرن. ثم بالنسبة للبوابة في FaxNET كان من الضروري إنشاء العديد من المرافق عبر
تجربتي السابقة في التعامل مع الأخطاء في sendmail وuucp/uupc أضافت إلى اجتهادي في "التعامل الشامل مع الأخطاء". ليس هناك فائدة من الغوص في تفاصيل تلك القصة، لكنني كافحت مع Heisenbug لمدة أسبوعين لمدة 10-14 ساعة. لذلك تذكرت، وبالأمس توقف هذا التعارف القديم للزيارة مرة أخرى.
TL ؛ إجابة الدكتور
فائدة 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
فإنه يسأل من هو، وإذا كان الخيار غير معتمد، ثم يتم إدراج كعب "الرجاء استخدام المترجم المتوافق مع دول مجلس التعاون الخليجي أو 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