دیدهاید که چگونه دستورهای ساده بنویسیم؛ دستوری که پس از آن آرگومانها میآیند. حالا میبینیم که چگونه با ترکیب دستورهای ساده، دستورهای پیچیدهتری بسازیم.
پیش از شروع، یک معرفی کوتاه دربارهی ورودی/خروجی.
فرایندها «کانالهای ورودی/خروجی استاندارد» دارند.
دستور tr نمونهی بسیار خالصی از این موضوع است.
تنها کاری که میکند این است که متن را از ورودیاش میخواند، نویسهها را ترانویسی میکند و متن حاصل را به خروجیاش میفرستد.
بعداً در برنامهی درسی، بیشتر دربارهی کار با کانالهای «stdio» خواهیم دید.
این یکی از ویژگیهای برجستهی برنامهنویسی شل است. پایپلاینها به شما اجازه میدهند که تبدیلهای پیچیدهای روی جریانی از متن انجام دهید.
برای تولید فهرستی مرتب از کاربران:
cat /etc/passwd | cut -d : -f 1 | sort
نماد پایپ (|) خروجی یک دستور را به ورودی دستور دیگر وصل میکند.
cut خروجی cat را میخواند و sort خروجی cut را میخواند.
بهطور پیشفرض، هر دستور در یک پایپلاین در یک زیرشل جداگانه اجرا میشود. (زیرشل یک فرایند فرزند است که نسخهای از شل در حال اجراست.)
اجرای پایپلاینها هزینهی کارایی دارد.
اگر پایپلاینهای طولانیای از دستورهای مشابه دارید، بهتر است همه را در یک دستور ترکیب کنید.
برای نمونه، پایپلاینهایی که از چند نمونه از grep، cut، sed، awk و tr استفاده میکنند، معمولاً میتوانند برای کارایی بیشتر در یک دستور awk واحد ترکیب شوند.
وضعیت خروج یک پایپلاین، وضعیت خروج آخرین دستور در آن پایپلاین است. یعنی اگر دستورهای قبلی شکست خورده باشند اما دستور آخر موفق شده باشد، وضعیت خروج کل پایپلاین صفر است.
تنظیمی در شل وجود دارد که میتواند این را تغییر دهد؛
اگر تنظیم «pipefail» فعال باشد (با set -o pipefail)، وضعیت خروج پایپلاین چنین خواهد بود:
فهرست دستور، دنبالهای از پایپلاینهاست که با ; (یا خط جدید)، && یا || از هم جدا میشوند.
A; B یک فهرست دستور است که در آن B پس از پایان A اجرا میشود.A && B، که در آن B تنها در صورتی اجرا میشود که A موفق شود (با وضعیت صفر خارج شود).A || B، که در آن B تنها در صورتی اجرا میشود که A شکست بخورد (با وضعیت غیرصفر خارج شود).وضعیت خروج یک فهرست دستور، وضعیت خروج آخرین دستوری است که اجرا شده است.
عملگرهای && و || را میتوان زنجیرهای به کار برد، طوری که دستور بعدی بر اساس وضعیت دستورهای پیشین بهصورت شرطی اجرا شود.
برای نمونه
A && B && C && D || E
از این عملگرهای منطقی کم استفاده کنید. آنها میتوانند بهسرعت به کدی ناخوانا یا منطقی دشوارفهم منتهی شوند.
برای نمونه، ممکن است فکر کنید این دو یکی هستند:
if A; then B; else C; fi
A && B || C
تفاوت اینجاست: C چه زمانی اجرا میشود؟
در ادامه چند نمونه میبینید که فهرستهای دستور میتوانند کد را سادهتر کنند.
فرض کنید فایل دادهای دارید که دادههایی دربارهی مثلثها در آن است و هر مثلث با سه خط جداگانه نمایش داده میشود که اضلاع آن مثلث را در خود دارند.
$ cat triangle.dat
3
4
5
9
12
14
میتوانید از یک حلقهی while استفاده کنید که شرطش سه دستور read جداگانه است:
while read a && read b && read c; do
if is_pythagorean "$a" "$b" "$c"; then
echo "$a:$b:$c is pythagorean"
else
echo "$a:$b:$c is not pythagorean"
fi
done < triangle.dat
با فرض اینکه is_pythagorean دستوری باشد که تعیین میکند آیا سه ضلع در معادلهی فیثاغورس صدق میکنند یا نه، خروجی چنین خواهد بود:
3:4:5 is pythagorean
9:12:14 is not pythagorean
بسیاری از زبانهای برنامهنویسی نوعی ادعا دارند که در آن اگر شرطی برآورده نشود، یک استثنا پرتاب میشود
assert(user == "Administrator", "wrong user");
میتوانیم از یک عملگر OR در Bash استفاده کنیم تا آن تابع را شبیهسازی کنیم:
die () { # a little convenience function
echo "$*" >&2
exit 1
}
[[ $user == "Administrator" ]] || die "wrong user"
[[ $password == "secret!" ]] || die "incorrect password"
فهرستهای دستور طولانی خیلی زود ناخوانا میشوند. استفادهی فراوان از خطوط جدید میتواند بسیار کمک کند.
نمونهای را در نظر بگیرید که در آن اگر دو شرط برآورده شوند، واژهای به یک آرایه اضافه میشود.
[[ "$word" != "$topic" ]] && [[ "$key" == "$(sorted "$topic")" ]] && anagrams+=("$candidate")
Bash به شما اجازه میدهد پس از یک پایپ یا یک عملگر منطقی، خط جدید اضافه کنید.
[[ "$word" != "$topic" ]] &&
[[ "$key" == "$(sorted "$topic")" ]] &&
anagrams+=("$candidate")
اما ممکن است عملگر در انتهای خط بهراحتی از چشم بیفتد. استفاده از ادامهی خط یعنی میتوانید عملگر را اول قرار دهید، و این واضحتر نشان میدهد که فهرست ادامه دارد:
[[ "$word" != "$topic" ]] \
&& [[ "$key" == "$(sorted "$topic")" ]] \
&& anagrams+=("$candidate")
ادامهی خط دنبالهی دو نویسهای «بکاسلش» و «خط جدید» است (\ + \n).
وقتی Bash این دنباله را میبیند، بهسادگی از کد حذف میشود و بدینترتیب خط جاری با خط بعدی ادامه مییابد.
مراقب باشید بین بکاسلش و خط جدید هیچ فاصلهای نگذارید.
نمونهی دیگری هم هست
printf "%s\n" "${numbers[@]}" | bc --mathlib | sort --general-numeric-sort
یا
printf "%s\n" "${numbers[@]}" \
| bc --mathlib \
| sort --general-numeric-sort