Bashهیچ روش درونی برای استفاده از قالب ها( templates) ندارد. اگر چه، نسبت به اهداف واقعی خود، ممکن است فقط بتوانید شگرد ارزیابی فایل با یک bash جدید یا پوسته نمونه را به کار ببرید.
به هر حال، این مطلب در صفحه TemplateFiles بحث شده است.
پرسش و پاسخ 110 (آخرین ویرایش 2013-03-18 18:07:40 توسط 12)
به طور معمول موقعی که اشخاص این پرسش را مطرح میکنند، به دلیل آنست که میخواهند خطاهای کاربر را تشخیص داده و پیغام مناسبی برای آن تهیه کنند. یک نظریهای هست که میگوید نباید کاربر یونیکس را به این طریق نازپرورده نمود، و اگر کاربری حقیقتاً بخواهد در عوض منبع کردن اسکریپت شما، آن را اجرا کند،شما نباید برای این کار او را نکوهش کنید. حال از این که بگذریم، ما میتوانیم پرسش را به نحو دیگری که در واقع باید پرسیده شود، بیان کنیم:
من میخواهم اگر کاربر به جای منبع کردن اسکریپت آنرا از پوسته محاورهای اجرا نماید، یک پیغام خطا نمایش داده و از اجرای اسکریپت صرف نظر گردد.
کلید در اینجا، و دلیل آنکه من پرسش را به این طریق جملهبندی مجدد نمودهام، آنست که شما عملاً نمیتوانید آنچه را کاربر تایپ نموده است تعیین کنید، اما میتوانید تعیین کنید آیا کُد در حال تفسیر شدن با یک پوسته محاورهای است. این کار را باکنترل یک i در محتوای متغیر $- انجام بدهید:
# POSIX(?) case $- in *i*) : ;; *) echo "You should dot me in" >&2; exit 1;; esac
یا با استفاده از ترکیب دستوری غیر-POSIX:
# Bash/Ksh if [[ $- != *i* ]]; then echo "You should dot me in" >&2; exit 1 fi
البته، این مورد در حالتهای ترفندآمیزی مانند «من میخواهم فایل من از یک اسکریپت غیرمحاورهای نقطهای بشود...» کار نمیکند. برای آن حالتها، پاراگراف اول این صفحه را ببینید.
پرسش و پاسخ 109 (آخرین ویرایش 2012-09-11 07:43:44 توسط dslb-088-072-055-065)
برای انجام این کار روشهای بیشماری موجود است، اما تمام آنها محدود به ابزارهای در دسترس هستند. من به چاره سازیهای زیر رسیدم .
اگر میخواهید در انتظار تمام پردازشهای فرزند بمانید، به سادگی wait را بدون شناسه احضار کنید.
اگر فقط میخواهید منتظر برخی، اما نه تمام آنها، بمانید و نگران وضعیت خروج آنها نمیباشید، میتوانید wait را با PIDهای چندگانه فراخوانی کنید:
wait $pid1 $pid2
اگر نیاز دارید، بدانید آیا فرزند موفق یا ناموفق بوده، آنوقت شاید:
waitall() { # PID... ## .انتظار برای خروج فرزند و نمایش آنکه آیا همه با وضعیت صفر خارج شدهاند local errors=0 while :; do debug "Processes remaining: $*" for pid in "$@"; do shift if kill -0 "$pid" 2>/dev/null; then debug "$pid is still alive." set -- "$@" "$pid" elif wait "$pid"; then debug "$pid exited with zero exit status." else debug "$pid exited with non-zero exit status." ((++errors)) fi done (("$#" > 0)) || break # متوقف گردد؟ sleep وقتی فرزند خاتمه داده میشود، چطور این :برای انجام sleep ${WAITALL_DELAY:-1} done ((errors == 0)) } debug() { echo "DEBUG: $*" >&2; } pids="" for t in 3 5 4; do sleep "$t" & pids="$pids $!" done waitall $pids
حلقهزنی بواسطه kill -0 میتواند بسیار بیکفایت باشد.
اطلاعات مفیدتر میتواند در صفحه مدیریت پردازش یافت شود.
پرسش و پاسخ 108 (آخرین ویرایش 2011-09-12 23:03:28 توسط GreyCat)
روشهای بیشماری برای انجام این کار هست، اما تمام آنها یا به ابزارهای در دسترس محدود میشوند، یا کُند هستند. ما چند نمونه را نشان خواهیم داد.
بیایید اول با روش آهسته قابل حمل شروع کنیم و با این نمونه آن را انجام بدهیم:
# POSIX while IFS= read -r line; do echo "$(date +%Y%m%d-%H:%M:%S) $line" done
و یکی دیگر که حتی کُندتر است:
awk '{system("printf \"`date +%T ` \">&2")}$0'
و سومی، که به طور جزئی سریعتر است، اما ممکن است بعضی از سطرهای ورودی را خُرد کند:
xargs -I@ -n1 date "+%T @"
اشکال واضح تمام مثالهای فوق آنست که ما در حال اجرای فرمان خارجی date برای هر سطر از ورودی میباشیم. اگر ما در هر دو ثانیه فقط یک سطر دریافت کنیم، شاید این پذیرفتنی باشد. اما اگر ما در حال تلاش برای مُهر تاریخ زدن به جریانی باشیم که در هر ثانیه سطرهای بسیاری تحصیل میکند، حتی ممکن است قادر به همگام شدن با نویسنده نباشیم.
روشهای متنوعی برای انجام این کار، بدون تولید پردازش فرزند(forking) به ازای هر سطر، وجود دارد، اما تمام آنها نیازمند ابزارهای غیر استاندارد یا پوستههای خاص، میباشند. Bash 4.2 میتواند آن را با printf انجام بدهد:
# Bash 4.2 while read -r; do printf "%(%Y%m%d-%H:%M:%S)T %s\n" -1 "$REPLY" done
مشخص کننده قالب %(...)T در bash نگارش 4.2 جدید است. شناسه -1 به او میگوید از زمان جاری به جای زمان عبور داده شده به عنوان شناسه، استفاده کند. برای جزئیات، صفحه man مربوط را ملاحظه نمایید.
یک روش دیگر، نوشتن یک perl یک سطری است:
perl -p -e '@l=localtime; printf "%04d%02d%02d-%02d:%02d:%02d ", 1900+$l[5], $l[4], $l[3], $l[2], $l[1], $l[0]'
مطمئن هستم شخصی با یک جایگزین 7 بایتی پیش خواهد آمد که همان کار را با استفاده از ترکیب دستوری جادویی پرل، که من هرگز قبلاً ندیدهام و نمیتوانم بفهمم، انجام میدهد، ....
ابزارهای دیگری مخصوصاً برای نشانهگذاری زمان فایلهای ثبت وقایع و مشابه، وجود دارد. یکی از آنها multilog از daemontools میباشد، اما قالب نشانهگذاری زمان آن TAI64N است که قابل خواندن انسانی نیست. دیگری ts از بسته moreutils میباشد.
پرسش و پاسخ 107 (آخرین ویرایش 2012-05-19 12:23:00 توسط GreyCat)
این مورد نیازمند برخی دستکاریهای مهارتآمیز توصیفگرفایل ، و یکی از موارد لوله دارای نام یا جایگزینی پردازشِ Bash میباشد. ما میخواهیم بر ترکیب دستوری Bashتمرکز نماییم.
اجازه بدهید با سادهترین حالت شروع کنیم: میخواهم خروجی استانداردم را علاوه بر صفحه نمایش به یک فایل ثبت رخداد tee نمایم.
این به معنای آنست که از هر آنچه به stdout ارسال میگردد دو نسخه میخواهیم -- یک نسخه برای صفحه نمایش(یا هر جایی که stdout موقعی که اسکریپت شروع شده به آنجا اشاره مینمود)، و یک نسخه برای فایل ثبت وقایع. برنامه tee برای این منظور استفاده میشود:
# Bash exec > >(tee mylog)
ترکیب دستوری جایگزینی پردازش، یک لوله با نام (یا چیزی قابل قیاس با آن) ایجاد میکند و برنامه tee را با خواندن از آن لوله در پس زمینه اجرا میکند. tee دو کپی از هر آنچه میخواند، ایجاد میکند -- یکی برای فایل mylog (که آن را باز میکند)، و یکی برای stdout، که از اسکریپت ارث میبرد. سرانجام، exec خروجی استاندارد پوسته را به لوله تغییر مسیر میدهد.
به علت اینکه یک job پس زمینه وجود دارد، که باید تمام خروجی را قبل از اینکه ما آن را ببینیم، بخواند و پردازش کند، این مورد مقداری تأخیر ناهماهنگ نشان میدهد. حالتی مانند این مورد را ملاحظه نمایید:
# Bash exec > >(tee mylog) echo "A" >&2 cat file echo "B" >&2
سطر A و B که در stderr نوشته میشوند به میان پردازش tee نمیروند - آنها مستقیماً به stderr ارسال میشوند. به هرحال، file که ما از cat به دست میآوریم، قبل از اینکه ما آن را ببینیم به میان لوله و tee فرستاده میشود. اگر ما این اسکریپت را بدون هر گونه تغییر مسیر در ترمینال اجرا نماییم، احتمالاً (تضمین نمیشود!) چیزی مانند این میبینیم:زیرنویس مترجم 1
~$ ./foo A B ~$ hi mom
حقیقتاً راهی برای اجتناب از این وجود ندارد. ما میتوانستیم با امید به خوشاقبالی، stderr را به سَبک مشابهی به تأخیر اندازیم، اما تضمینی وجود ندارد که سطرها به طورمساوی به تأخیر اُفتند.
همچنین، توجه نمایید که محتویات فایل بعد از اعلان بعدی پوسته چاپ گردید. بعضی اشخاص آن را اختلال نظم احساس میکنند. یکبار دیگر، روش پاکیزهای برای اجتناب از آن وجود ندارد، چون tee در یک پردازش پسزمینه، اما خارج از کنترل ما انجام شده است. حتی اضافه نمودن فرمان wait به اسکریپت تأثیری ندارد. برخی افراد جهت دادن شانس به فرمان tee پس زمینه برای پایان یافتن، فرمان sleep 1 را به انتهای اسکریپت اضافه میکنند. این کار میکند(معمولاً)، اما بعضی اشخاص آن را بدتر از مشکل اصلی میدانند.
اگر از دستور زبان Bash دوری کنیم، و لوله با نام و یک پردازش پس زمینه خودمان را تنظیم کنیم، آنوقت کنترل را به دست میآوریم:
# Bash mkdir -p ~/tmp || exit 1 trap 'rm -f ~/tmp/pipe$$; exit' EXIT mkfifo ~/tmp/pipe$$ tee mylog < ~/tmp/pipe$$ & pid=$! exec > ~/tmp/pipe$$ echo A >&2 cat bar echo B >&2 exec >&- wait $pid
بازهم یک ناهمزمانی میان stdout و stderr وجود دارد، اما حداقل به اندازهای نیست که بعد از آنکه اسکریپت خارج شده است در ترمینال بنویسد:
~$ ./foo A B hi mom ~$
این به گونه بعدی این پرسش منجر میگردد -- میخواهم stdout و stderr با حفظ هماهنگی سطرها، هردو را با یکدیگر، ثبت کنم.
این یکی نسبتاً آسان است، به شرطی که دلواپس بهمریختگی تمایز مابین stdout و stderr روی ترمینال نباشیم. ما فقط یکی از توصیفگرهای فایل را دوگانه میسازیم:
# Bash exec > >(tee mylog) 2>&1 echo A >&2 cat file echo B >&2
در حقیقت، حتی آسانتر از پرسش اصلی است. همه چیز به طور صحیح هماهنگ میشود، هم در ترمینال هم در فایل ثبت وقایع:
~$ ./foo A hi mom B ~$ cat mylog A hi mom B ~$
اگرچه، بازهم احتمال آمدن قسمتی از خروجی پس از اعلان فرمان پوسته وجود دارد:
~$ ./foo A hi mom ~$ B
(این مورد میتواند با همان راه حلِ لوله با نام و پردازش پس زمینه که قبلاً نشان دادم حل بشود.)
سومین گونه از این پرسش نیز نسبتاً ساده است: من میخواهم خروجی استاندارد را در یک فایل ثبت کنم، و stderr را در فایل دیگر. این ساده است، زیرا آن محدودیت اضافی را نداریم که میباید هماهنگی مابین دو جریان روی ترمینال را اداره نماییم. فقط نویسندههای فایلهای log را تنظیم میکنیم:
exec > >(tee mylog.stdout) 2> >(tee mylog.stderr >&2) echo A >&2 cat bar echo B >&2
و اکنون جریانهای ما به طور جداگانه ثبت میشوند. چون فایلهای لاگ مجزا میباشند، ترتیبی که سطرها در آنها نوشته میشوند، اهمیت ندارد. هرچندکه، در ترمینال نتایجِ درهم ریخته حاصل خواهیم نمود:
~$ ./foo A hi mom B ~$ ./foo hi mom A B ~$
اما بعضی اشخاص نمیخواهند از دست دادن تمایز میان خروجی استاندارد و stderr، یا ناهماهنگی سطرها را قبول کنند. آنها در مورد کلمات وسواس دارند، و بنابراین متقاضی مشکلترین گونه این پرسش هستند -- من میخواهم stdout و stderr را با یکدیگر داخل یک فایل منفرد ثبت نمایم، اما همچنین میخواهم آنها را در مقصدهای جداگانه اصلی آنها نگهداری کنم.
به منظور انجام این مورد، نخست باید چند نکته را متذکر شویم:
راهی برای نوشتن یک پردازش در اسکریپت پوستهای که از دو توصیفگر فایل جداگانه بخواند که یکی از آنها ورودی در دسترس دارد، وجود ندارد زیرا پوسته دارای میانجی poll(2) یا select(2) نیست.
بنابراین، ما به دو پردازش نویسنده جداگانه نیاز داریم.
تنها روش حفظ نمودن خروجی دو نویسنده جداگانه، از خراب شدن با یکدیگر، ایجاد اطمینان از آن است که هردو نویسنده، خروجی خود را در وضعیت افزودن (درج) باز کنند. یک FD که در وضعیت افزودن باز میشود صفت تضمین کنندهای دارد، که هر گاه داده میخواهد در آن نوشته شود، نخست به انتها میپرد.
بنابراین:
# Bash > mylog exec > >(tee -a mylog) 2> >(tee -a mylog >&2) echo A >&2 cat file echo B >&2
این تضمین میکند که فایل ثبت رخداد صحیح است. تضمین نمیکند که نویسندهها قبل از اعلان فرمان بعدی خاتمه یابند:
~$ ./foo A hi mom B ~$ cat mylog A hi mom B ~$ ./foo A hi mom ~$ B
میتوانستیم همان ترفند لوله با نام بعلاوه waitرا استفاده کنیم که قبلاً به کار بردیم(به عنوان تمرین برای خواننده واگذار شد).
این صفحه پرسش «آیا سطرهایی که در ترمینال ظاهر میگردند تضمین میشود که به ترتیب صحیح ظاهر شوند» را باقی میگذارد. در حال حاضر: حقیقتاً من نمیدانم.
پرسش و پاسخ 106 (آخرین ویرایش 2013-03-28 16:00:45 توسط GreyCat)
مترجم: در اینجا فرض شده است که شما دارای فایلی به نام file در پوشه جاری هستید که محتوی رشته hi mom میباشد و همچنین کد فوق را در اسکریپتی به نام foo ذخیره نمودهاید. پس از اجرای اسکریپت فایلی به نام mylog با همان محتوا در دایرکتوری جاری خواهید داشت، و این نشان دهنده آن است که محتوای فایل file در دونسخه یکی برای خروجی استاندارد و دیگری به فایل mylog ارسال گردیده است. (بازگشت)