کدهای خروجی چیستند؟
زمانی که یک فرمان لینوکس را اجرا میکنید، ممکن است با شکست مواجه شود. دلایل مختلفی میتواند باعث بروز مشکل شود، اما معمولاً پیامی دریافت میکنید که توضیح میدهد چرا:
پیام خطای لینوکس از دستور ls
که میگوید “فایل یا دایرکتوری مورد نظر وجود ندارد.”
پیام خطایی که مشاهده میکنید، در جریان STDERR نمایش داده میشود که متفاوت از جریان STDOUT است و به شما این امکان را میدهد که خطاها را از خروجی مورد انتظار جدا کنید. به عنوان یک کاربر، توصیف خطا باید آموزنده باشد، اما اسکریپتها و سایر فرآیندهای خودکار به طور قابل اعتماد به اطلاعات خطا نیاز دارند.
در کنار پیامهای خطای قابل خواندن، هر دستور یک کد خروجی نیز برمیگرداند. این کد عددی بین ۰ تا ۲۵۵ است که نشان میدهد آیا دستور با موفقیت اجرا شده یا خیر. به طور پیشفرض، کد خروجی پنهان است، اما شل شما دسترسی به این مقدار را از طریق متغیر ویژه $?
فراهم میکند.
توجه داشته باشید که مقدار ۰ نشاندهنده موفقیت و مقدار غیرصفر به معنی شکست است. شما میتوانید این مقدار را در خط فرمان بررسی کنید، در یک اسکریپت آن را بررسی کنید و از آن برای کنترل جریان برنامههایتان استفاده کنید.
چگونه از کدهای خروجی استفاده کنم؟
در مثال بالا، دستور ls
زمانی که موفق بود کد خروجی ۰ را باز میگرداند و زمانی که آرگومان آن یک فایل معتبر نبود، کد خروجی ۱ را میدهد. این ممکن است غیرقابل انتظار به نظر برسد و با زبانهای برنامهنویسی معمولی که ۰ را به عنوان یک مقدار نادرست و ۱ را به عنوان درست در نظر میگیرند، مغایرت دارد. با این حال، این تنظیمات اجازه میدهد تا یک مقدار جهانی برای موفقیت و طیف وسیعی از مقادیر برای نشان دادن انواع مختلف خطاها استفاده شود.
علاوه بر نمایش متغیر $?
برای بررسی دستی وضعیت خروجی، میتوانید از آن در یک دستور شرطی if
استفاده کنید. اگر دستور کد خروجی ۰ بازگرداند، دستور then
اجرا خواهد شد. در غیر این صورت، اگر دستور else
وجود داشته باشد، آن اجرا میشود. برای مثال:
if command; then echo "command succeeded"; else echo "command failed"; fi
شما میتوانید از این ساختار در یک اسکریپت شل استفاده کنید، جایی که فرمتبندی آن برای خوانایی راحتتر است:
if command
then echo "command succeeded"
else echo "command failed"
fi
این معادل کد زیر است:
command
if [ $? -eq 0 ]
then echo "command succeeded"
else echo "command failed"
fi
نسخه طولانیتر از سینتکس کروشه برای آزمایش مقدار $?
استفاده میکند، اما معمولاً آزمایش مقدار مستقیماً، مانند مورد اول، آسانتر است. به یاد داشته باشید که $?
همیشه نمایانگر کد خروجی از آخرین دستور است، بنابراین باید مراقب باشید. ممکن است وسوسه شوید که مقدار $?
را برای اشکالزدایی نمایش دهید، مانند:
command
echo "command returned exit code:" $?
if [ $? -eq 0 ]
then echo "command succeeded"
else echo "command failed"
fi
اما در نقطه بررسی شرط، $?
مقدار وضعیت خروجی دستور echo
را خواهد داشت که تقریباً همیشه ۰ خواهد بود. برای جلوگیری از این مشکل، میتوانید دستور را مستقیماً در شرط آزمایش کنید یا مقدار $?
را در یک متغیر ذخیره کنید.
چه چیزهای دیگری درباره وضعیت خروجی باید بدانید؟
کدهای خروجی نسبتاً ساده هستند و از اصول یونیکس پیروی میکنند، که این بخشی از قدرت آنهاست. اما چند نکته و موارد استثنایی وجود دارد که باید از آنها آگاه باشید.
کدهای خروجی از شل شما
شل شما حتی زمانی که دستوری که وارد میکنید به درستی اجرا نشود، از کدهای خروجی استفاده میکند. برای مثال، zsh
و bash
از کد ۱۲۷ استفاده میکنند اگر دستور پیدا نشود و از کد ۱۲۶ اگر دستور قابل اجرا نباشد:
کدهای خروجی غیرمعمول
برخی دستورات قوانین وضعیت خروجی را کمی تغییر میدهند؛ در واقع اینها فقط کنوانسیونها هستند. به عنوان مثال، دستور diff
از کد ۰ برای به معنی “هیچ تفاوتی نیست”، از کد ۱ برای “تفاوتها موجود هستند” و از کد ۲ برای نشان دادن یک خطا استفاده میکند:
این میتواند معنای شرطها را کمی گیجکننده کند؛ به عنوان مثال:
if diff file1 file2; then
echo these files are the same!
fi
در اینجا، دستور diff
اگر هیچ تفاوتی پیدا نکند، کد خروجی ۰ (موفق) میدهد و بنابراین شرط شرطی به درستی اجرا میشود و پیام “این فایلها مشابه هستند!” نمایش داده میشود، حتی اگر فایلها هیچ تفاوتی نداشته باشند. این نوع رفتار میتواند گیجکننده باشد، به ویژه زمانی که به طور نادرست انتظار دارید که نتیجهٔ تفاوت فایلها به صورت کد خروجی غیرصفر برگردد.
دستور curl
هم کمی غیرمعمول است. شما ممکن است انتظار داشته باشید که curl
یک خطای HTTP، مانند ۴۰۴ برای یک URL غیرموجود، را به عنوان کد وضعیت غیر صفر گزارش دهد، اما اینطور نیست:
شما میتوانید از گزینه -f
(یا --fail
) برای تغییر رفتار دستور curl
استفاده کنید، به طوری که در صورت بروز خطا، کد خروجی ۲۲ را برگرداند:
با این حال، باید توجه داشته باشید که این موضوع ۱۰۰٪ تضمین شده نیست و برخی از خطاهای HTTP، از جمله آنهایی که به احراز هویت نیاز دارند، همچنان کد خروجی ۰ را برمیگردانند.
استفاده از کدهای خروجی در زنجیره دستورات
یکی از بهترین میانبرهای استفاده از کدهای خروجی زمانی اتفاق میافتد که شما حتی نیازی به فکر کردن در مورد آن ندارید. در اینجا یک مثال ساده آورده شده است:
ls program && ./program && echo success || echo epic fail
عملگرهای منطقی &&
و ||
به شما این امکان را میدهند که چندین دستور را بر اساس کد خروجی دستورات دیگر اجرا کنید. اینها مشابه عملگرهای مربوطه در بیشتر زبانهای برنامهنویسی هستند، اما به کنوانسیون کدهای خروجی احترام میگذارند. عملگر &&
فقط دستوری که در سمت راست آن است را اجرا میکند اگر دستور سمت چپ کد خروجی ۰ (موفقیت) داشته باشد. عملگر ||
فقط دستوری که در سمت راست آن است را اجرا میکند اگر دستور سمت چپ با کد غیر صفر (خطا) شکست بخورد.
یک حالت بسیار رایج زمانی اتفاق میافتد که شما نرمافزاری را از ابتدا میسازید، با استفاده از این دستورات:
./configure && make && make install
سه بخش این دستور به شرح زیر عمل میکنند:
./configure
یک اسکریپت محلی را اجرا میکند که محیط شما را بررسی کرده و یک فایل Makefile تولید میکند.make
برنامه make را اجرا کرده و نرمافزار را با استفاده از Makefile ساخته و کامپایل میکند.make install
اجرایی که تولید شده را به مکانی شناخته شده مانند/usr/local/bin
کپی میکند.
اگر هر یک از بخشهای این فرآیند شکست بخورد، بلافاصله متوقف میشود و باقی مراحل اجرا نمیشوند.
دستورات true
و false
سیستمعامل لینوکس شما شامل دو دستور جالب به نامهای true
و false
است. هرکدام از این دستورات هیچ کاری انجام نمیدهند، به جز اینکه یک کد خروجی مناسب باز میگردانند: ۰ و ۱ به ترتیب.
ممکن است این دستورات به نظر خیلی مفید نیایند، اما این دستورات در آزمایشها و برخی وظایف اسکریپتنویسی به کار میآیند. میتوانید از دستور true
در یک دستور while
برای ایجاد یک حلقه بینهایت استفاده کنید:
while true
do
echo "در حال اجرا تا زمانی که ^c را بزنید"
sleep 10
done
همچنین میتوانید از این دستورات همراه با اپراتورهای منطقی برای تغییر رفتار دستورات استفاده کنید. به عنوان مثال، میتوانید به طور موقت از اجرای یک زنجیره دستورات طولانی جلوگیری کنید:
false && (none || of-these && commands || will-run)
یا میتوانید یک زنجیره دستورات را مجبور کنید ادامه یابد، حتی اگر یک دستور شکست بخورد:
cat file-that-may-not-exist | true && echo done
بازگشت کد خروجی از اسکریپتهای خودتان
شما احتمالاً قبلاً از دستور exit
برای خروج از ترمینال استفاده کردهاید. به طور فنی، این دستور شل شما را متوقف میکند و به این معناست که میتوانید از آن برای متوقف کردن یک اسکریپت شل نیز استفاده کنید. به طور پیشفرض، اسکریپتهای شما با همان کدی که آخرین دستور باز میگرداند، خاتمه مییابند.
اما شما میتوانید کد خروجی را با ارسال یک پارامتر عددی به دستور exit
تغییر دهید:
exit number
اسکریپت شما حالا با کد وضعیت که شما ارائه دادهاید، خاتمه مییابد و میتوانید از این برای اطلاعرسانی شرایط خطای مختلف از برنامهتان به دیگران استفاده کنید.
وقتی که با آنها آشنا شوید، دستورات خروجی بسیار ساده برای استفاده هستند. شما حتی ممکن است بدون اینکه به آنها فکر کنید از آنها استفاده کنید، اما وقتی به نوشتن اسکریپت میرسید، مطمئن شوید که از کدهای خروجی به درستی استفاده میکنید.