خوب، بستگی دارد به اینکه آیا میخواهید خروجی فرمان را ذخیره کنید(هر یک از stdout یا stdout + stderr) یا وضعیت خروج آن(0 تا 255، به طور نوعی در ازای 0 به معنی موفقیت).
اگر میخواهید خروجی را تصرف نمایید، از جایگزینی فرمان استفاده کنید:
output=$(command) # stdout only; stderr remains uncaptured output=$(command 2>&1) # both stdout and stderr will be captured
اگر وضعیت خروج را میخواهید، از پارامتر ویژه $? بعد از اجرای فرمان استفاده نمایید:
command status=$?
اگر هر دو را میخواهید:
output=$(command) status=$?
تخصیص به output تأثیری بر وضعیت خروج command، که بازهم در متغیر $? است، ندارد.
اگر در واقع نمیخواهید وضعیت خروج را ذخیره کنید، اما میخواهید بر مبنای موفقیت یا شکست، عملی انجام شود، فقط از if استفاده نمایید:
if command; then echo "it succeeded" else echo "it failed" fi
یا اگر میخواهید خروجی استاندارد را ذخیره کنید و بدون ذخیره نمودن یا بررسی $? به طور صریح، عملی نیز نسبت به موفقیت یا شکست، انجام شود، از این:
if output=$(command); then echo "it succeeded" ...
اگر وضعیت خروج یک فرمان از خطلوله را میخواهید، چطور؟ اگر وضعیت خروج آخرین فرمان را میخواهید، مشکلی نیست-- در متغیر $? است، درست مثل قبل. اگر وضعیت خروج بعضی از فرمانهای دیگر را میخواهید، از آرایه PIPESTATUS (منحصر به BASH )استفاده کنید. بر فرض که شما وضعیت خروج فرمان grep در کد زیر را میخواهید:
grep foo somelogfile | head -5 status=${PIPESTATUS[0]}
Bash نگارش 3.0 گزینه pipefail را نیز اضافه نموده ، که اگر شما واقعاً میخواهید در نتیجه شکست grep عملی انجام شود، میتواند استفاده شود:
set -o pipefail if ! grep foo somelogfile | head -5; then echo "uh oh" fi
حالا، چند مورد زیرکانه. بیایید فرض کنیم فقط خروجی استاندارد خطا را میخواهید، اما خروجی استاندارد را نمیخواهید. خوب، پس اول باید تصمیم بگیرید که میخواهید خروجی استاندارد به کجا برود:
output=$(command 2>&1 >/dev/null) #.خروجی خطا ذخیره و خروجی صرفنظر میشود output=$(command 2>&1 >/dev/tty) #. ذخیره و خروجی به ترمینال ارسال میشود stderr output=$(command 3>&2 2>&1 1>&3-) #.اسکریپت میرودstderr ذخیره و خروجی به stderr
اگر چه به طور قابل ملاحظهای دشوارتر، اما برگشت دادن خروجی به جایی که بدون وجود تغییر مسیر میرفت، امکان پذیر است. این ذخیره محتوای فعلی خروجیاستاندارد را در بر میگیرد، به طوری که میتواند داخل جایگزینی فرمان به کار برده شود:
exec 3>&1 # به آن اشاره میکند stdout(1) ذخیره مکانی که output=$(command 2>&1 1>&3) # ذخیره میشود stderr .اجرای فرمان exec 3>&- # شماره 3 FD بستن # :را با اجازه عبور به خروجی از میان آن، ذخیره میکند stderr یا این جایگزین که { output=$(command 2>&1 1>&3-) ;} 3>&1
توجه نمایید، که در آخرین مثال فوق 1>&3- توصیفگر فایل 3 را دو نسخهای میکند و یک نسخه از آن را در FD شماره 1 ذخیره مینماید، و FD شماره 3 را میبندد. که به این شکل نیز میتواند نوشته شود 1>&3 3>&-.
آنچه که نمیتوانید انجام بدهید، ذخیره خروجی استاندارد در یک متغیر و stderr در دیگری، با استفاده ازتغییر مسیر FD به تنهایی است. شما باید از یک فایل موقت(یا لوله با نام) برای رسیدن به آن یک، استفاده نمایید.
خوب، میتوانید یک شکاف ناخوشآیند(مترجم: منظور جمله« ... this line is » میباشد) به کار ببرید:
result=$( { stdout=$(cmd) ; } 2>&1; echo "this line is the separator"; echo "$stdout") var_out=${result#*this line is the separator$'\n'} var_err=${result%$'\n'this line is the separator*}
به طور واضح، این قوی نیست، به دلیل آنکه هم خروجی استاندارد و هم خطای استاندارد فرمان میتوانند شامل هر تعداد جداکننده رشتهای که شما به کار بردهاید، باشند.
و اگر شما کُد وضعیت cmd خود را خواسته باشید(این اصلاح برای حالتی است که اگر خروجی cmd تهی باشد)
cmd() { curl -s -v http://www.google.fr; } result=$( { stdout=$(cmd); returncode=$?; } 2>&1; echo -n "this is the separator"; echo "$stdout"; exit $returncode) returncode=$? var_out=${result#*this is the separator} var_err=${result%this is the separator*}
یادداشت: پرسش اصلی، «چگونه میتوانم مقدار برگشتی دستوری را در متغیری ذخیره کنم؟» بود. این کلمه به کلمه یک پرسش واقعی، پرسیده شده در #bash است، مبهم و کلی.
پرسش و پاسخ 2 (آخرین ویرایش 2012-12-04 09:50:10 توسط geirha)