از فرمان comm(1) استفاده کنید:
# Bash # file2 و file1 فصل مشترک # (یعنی فقط سطرهایی که در هر دو فایل وجود دارد) comm -12 <(sort file1) <(sort file2) # file2 از file1 تفریق # (وجود دارد file2 یعنی سطرهایی که منحصراً در) comm -13 <(sort file1) <(sort file2)
برای تفصیل بیشتر صفحات man دستور comm را بخوانید. آنها که آن بالا میبینید جایگزینی پردازشها هستند.
اگر بنا به دلایلی سیستم شما فاقد برنامه مرکزی comm است، میتوانید از این روشهای دیگر استفاده کنید. (در حقیقت، شما از هیچ کدام اینها واقعاً نباید استفاده کنید. این روشها توسط افرادی نوشته شدهاند که هنوز چیزی در باره comm نمیدانستند. اما پیشنهادات محرمانه برای دوستداران کُندی!)
# file2 و file1 فصل مشترک grep -xF -f file1 file2 # file2 از file1 تفریق grep -vxF -f file1 file2
این کُد با هر grep سازگار با POSIX کار خواهد کرد، در سیستمهای قدیمیتر ممکن است به جای grep -F نیاز به استفاده از fgrep داشته باشید.
# file2 و file1 فصل مشترک sort file1 file2 | uniq -d # (با این فرض که فایلها محتویات تکراری ندارند) # file1-file2 (تفریق) sort file1 file2 file2 | uniq -u # file2 - file1 (تفریق به همان طریق اما برعکس) sort file1 file2 file1 | uniq -u
sort file1 file1 file2 | uniq -c | awk '{ if ($1 == 2) { $1 = ""; print; } }'
# .نیستند را چاپ میکند file2 هستند و در file1 فقط سطرهایی که در # .با جابه جایی شناسهها نتیجه معکوس خواهید گرفت awk 'NR==FNR{a[$0];next} !($0 in a)' file2 file1 # .سطرهایی که در هر دو فایل هستند چاپ میشود، ترتیب شناسهها مهم نیست awk 'NR==FNR{a[$0];next} $0 in a' file1 file2
برای یک شرح در باره چگونگی کارکرد این مورد، مقایسه دو فایل توسط awk را ببینید.
پرسش و پاسخ 36 (آخرین ویرایش 2010-12-28 17:17:57 توسط GreyCat)
خوب، تا اندازه بسیاری بستگی به آن دارد که چه کاری میخواهید با آنها انجام بدهید. چندین راهکار ، هرکدام با ضعف و قوتهای مربوط به خود، وجود دارد.
فهرست مطالب
این راهکار هر مجموعهای از گزینههای اختیاری را مدیریت میکند، زیرا تجزیه کننده را خود شما مینویسید. برای 90% برنامهها، سادهترین رویکرد است(به دلیل آنکه شما به ندرت به موارد تفننی نیاز دارید).
این مثال ترکیبی از گزینههای کوتاه و بلند را مدیریت خواهد کرد. توجه کنید چگونه هر دو شکل "--file" و "--file=FILE" اداره میشوند.
1 #!/bin/sh
2 # POSIX ترکیب دستوری پوسته
3
4 # تنظیم دوباره تمام متغیرهایی که باید برقرار شوند
5 file=""
6 verbose=0
7
8 while :
9 do
10 case $1 in
11 -h | --help | -\?)
12 # شما usage()یا Help() فراخوانی تابع
13 exit 0 # را به کارنبرید exit 1 این یک خطا نیست، کاربر کمک خواسته
14 ;;
15 -f | --file)
16 file=$2 # را گرفتهاید FILE میتوانید بررسی کنید که واقعاً
17 shift 2
18 ;;
19 --file=*)
20 file=${1#*=} # "=" حذف هر چیز تا رسیدن به
21 shift
22 ;;
23 -v | --verbose)
24 # یکی به درازنویسی اضافه میکند -v هر نمونه از
25 verbose=$((verbose+1))
26 shift
27 ;;
28 --) # پایان تمام گزینه ها
29 shift
30 break
31 ;;
32 -*)
33 echo "WARN: Unknown option (ignored): $1" >&2
34 shift
35 ;;
36 *) # while گزینه دیگری موجود نیست، توقف حلقه
37 break
38 ;;
39 esac
40 done
41
42 # .به فرض که گزینههایی لازم باشند، کنترل آنکه آنها را گرفتهایم
43
44 if [ ! "$file" ]; then
45 echo "ERROR: option '--file FILE' not given. See --help" >&2
46 exit 1
47 fi
48
49 # .بقیه برنامه در اینجا
50 # اگر پس از گزینهها، (به عنوان مثال) فایلهای ورودی موجود باشند، آنها
51 # .باقی خواهند ماند"$@" در پارامترهای مکانی
52
این تجزیه کننده گزینههای جداگانهای که به یکدیگر الحاق گردیدهاند را مدیریت نمیکند(مانند -xvf که به عنوان -x -v -f قبول بشود). این مورد با تلاش میتوانست افزوده شود، اما به عنوان یک تمرین برای خواننده واگذار گردید.
برخی برنامه نویسان Bash دوست دارند، برای هشیاری در برابر متغیرهای استفاده نشده، این کُد را در ابتدای اسکریپتهای خودشان بنویسند:
set -u # set -o nounset یا
استفاده از این دستور حلقه فوق را ناموفق میکند، چون "$1" شاید درموقع ورود به حلقه برقرار نباشد. چهار راه حل برای این موضوع وجود دارد:
عدم استفاده از -u.
تعویض case $1 in با case ${1+$1} in (تمام کُدهای پس از آن را که set -u نقض میکند، به خوبی توانمند میسازد).
تعویض case $1 in با case ${1-} in (هر متغیری که امکان اعلان نشدن دارد، برای ممانعت از اثر set -u، میتواند به صورت ${variable-} نوشته بشود).
هرگز از getopt(1) استفاده نکنید. getopt نمیتواند شناسههای رشتهای تهی، یا شناسههای دارای فضای سفید را اداره کند. لطفاً اصلاً وجود آن را فراموش کنید.
پوسته POSIX (و سایرین) به جای آن getopts را ارائه مینمایند که برای استفاده مطمئن است. این هم یک مثال ساده getopts:
1 #!/bin/sh
2
3 # POSIX یک متغیر
4 OPTIND=1 # قبلاً در پوسته به کار رفته باشد getopts تنظیم مجدد در حالتی که
5
6 # :ارزش گذاری اولیه متغیرهای خودمان
7 output_file=""
8 verbose=0
9
10 while getopts "h?vf:" opt; do
11 case "$opt" in
12 h|\?)
13 show_help
14 exit 0
15 ;;
16 v) verbose=1
17 ;;
18 f) output_file=$OPTARG
19 ;;
20 esac
21 done
22
23 shift $((OPTIND-1))
24
25 [ "$1" = "--" ] && shift
26
27 echo "verbose=$verbose, output_file='$output_file', Leftovers: $@"
28
29 # انتهای فایل
30
مزایای getopts عبارتند از:
مواردی مانند -vf filename که روش یونیکسی مورد انتظار میباشد را به طور خودکار مدیریت میکند.
اشکال getopts آن است که فقط گزینههای کوتاه را مدیریت میکند(-h، نه --help).
یک آموزش getopts وجود دارد که شرح میدهد هر یک از ترکیبات دستوری و متغیرها به چه معنی هستند. در bash، همچنین help getopts نیز وجود دارد، که میتواند آموزنده باشد.
همچنین بازهم در فراخوانی getopts آن اشکال وجود دارد که گزینهها در حداقل دو، یا شاید سه مکان کُد میشوند، در جمله case که آنها را پردازش میکند و احتمالاً درپیغام راهنمایی که یکی از این روزها خواهان نوشتن آن میشوید. این یک فرصت کلاسیک برای رسوخ خطاها در کُدهای نوشته و نگهداری شده -غالباً هنوز زیاد توسعه نیافته، یا خیلی قدیمیتر- است. این مورد با استفاده از توابع فراخوانی برگشتی، میتواند پیشگیری بشود، اما این رویکرد نوعی خنثی نمودن هدف استفاده از getopts میباشد.
این هم یک مثال که مدعی تجزیه گزینههای بلند با getopts میباشد. ایده اصلی کاملاً ساده است: فقط "-:" را در optstring قرار بدهید. این ترفند به پوستهای نیاز دارد که گزینه-شناسه را اجازه دهد(یعنی نام فایل در "-f filename" به صورت "-ffilename" به گزینه الحاق بشود). استاندارد POSIX میگوید یک فاصله باید بین آنها باشد، bash و dash نوع "-ffilename" را اجازه میدهند، اما اگر کسی میخواهد اسکریپت قابل حمل بنویسد، نباید به این ارفاق تکیه کند.
1 #!/bin/bash
2 # .استفاده میکند. به طوری که نوشته شده قابل حمل نیست bash از ملحقات
3
4 optspec=":h-:"
5
6 while getopts "$optspec" optchar
7 do
8 case "${optchar}" in
9 -)
10 case "${OPTARG}" in
11 loglevel)
12 eval val="\$${OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
13 echo "Parsing option: '--${OPTARG}', value: '${val}'" >&2
14 ;;
15 loglevel=*)
16 val=${OPTARG#*=}
17 opt=${OPTARG%=$val}
18 echo "Parsing option: '--${opt}', value: '${val}'" >&2
19 ;;
20 esac
21 ;;
22 h)
23 echo "usage: $0 [--loglevel[=]<value>]" >&2
24 exit 2
25 ;;
26 esac
27 done
28
29 # End of file
30
در عمل، این مثال به قدری ابهام آلود است، که اگر این تنها دلیل استفاده از getopts باشد، شاید افزودن پشتیبانی از گزینه الحاق شده به حلقه تجزیه دستی (مانند -vf filename)، نسبت به آن قابل ترجیح باشد.
در اینجا نگارش بهبود یافته و عمومیتری از تلاش فوق برای افزودن، پشتیبانی ازگزینههای بلند هنگام استفاده از getopts، آوردهایم:
1 #!/bin/bash
2 # .استفاده میکند. به صورت نوشته شده قابل حمل نیست bash از ملحقات
3
4 declare -A longoptspec
5 longoptspec=( [loglevel]=1 ) # استفاده از آرایه انجمنی برای تعیین آنکه
# longlevelگزینه بلند چند شناسه را قبول میکند، در این حالت تعریف میکنیم که
#یک شناسه دارد یامیپذیرد، گزینههای بلندی که به این طریق لیست نشدهاند به طور
# پیش فرض تعداد شناسه آنها صفر است
6 optspec=":h-:"
7 while getopts "$optspec" opt; do
8 while true; do
9 case "${opt}" in
10 -) # میباشد value= نام گزینه بلند یا نام گزینه بلند OPTARG
11 if [[ "${OPTARG}" =~ .*=.* ]]
#فقط یک شناسه امکان پذیر است --key=value با این شکل
12 then
13 opt=${OPTARG/=*/}
14 OPTARG=${OPTARG#*=}
15 ((OPTIND--))
16 else
# شناسههای چندتایی امکان پذیر است --key value1 value2 با ساختار
17 opt="$OPTARG"
18 OPTARG=(${@:OPTIND:$((longoptspec[$opt]))})
19 fi
20 ((OPTIND+=longoptspec[$opt]))
21 continue # تنظیم گردیدند میتوانیم همان طور opt و OPTARG اکنون که
# گرفته بودیم، آنها را پردازش نماییم getopts که اگر شناسه بلند را از
22 ;;
23 loglevel)
24 loglevel=$OPTARG
25 ;;
26 h|help)
27 echo "usage: $0 [--loglevel[=]<value>]" >&2
28 exit 2
29 ;;
30 esac
31 break; done
32 done
33
34 # End of file
35
با این نسخه میتوانید گزینههای کوتاه و بلند را در کنار هم داشته باشید ونیازی به ویرایش کُد از سطر 10 تا 22 نخواهید داشت. این راه حل همچنین میتواند شناسههای چندگانه برای گزینههای بلند را اداره کند، فقط از ${OPTARG} یا ${OPTARG[0]} برای اولین شناسه استفاده کنید، و از ${OPTARG[1]} برای دومین، ${OPTARG[2]} برای سومین شناسه و به همین ترتیب. این مورد نیز همان کمبود نمونه ماقبل خود را دارد و قابل حمل نبوده، مختص bash است.
یک رویکرد دیگر کنترل گزینهها در دستورات if میباشد. تابعی مانند این یکی شاید مفید باشد:
1 #!/bin/bash
2
3 HaveOpt ()
4 {
5 local needle=$1
6 shift
7
8 while [[ $1 == -* ]]
9 do
10 # به معنی پایان گزینهها میباشد "--" مطابق قرارداد
11 case "$1" in
12 --) return 1 ;;
13 $needle) return 0 ;;
14 esac
15
16 shift
17 done
18
19 return 1
20 }
21
22 HaveOpt --quick "$@" && echo "Option quick is set"
23
24 # End of file
25
و این در صورتی کار میکند که اسکریپت به این شکل اجرا بشود:
اما بدون "-" یا با "--" در اولین شناسه متوقف میشود:
البته، این رویکرد(تکرار روی لیست شناسه در هر نوبتی که میخواهید یک مورد را کنترل کنید،) نسبت به فقط یکبار تکرار و تنظیم نشانه متغیرها، خیلی کمتر کارامد است.
همچنین گزینهها را در سرتاسر برنامه پخش میکند. گزینه لفظی --quick ممکن است، دور از هر نام گزینه دیگر، در صدها سطر از بدنه اصلی برنامه ظاهر شود. این کابوسی برای حفظ ونگهداری است.
bhepple استفاده از process-getopt ( با مجوز GPL ) را پیشنهاد میکند و این نمونه کد را ارائه مینماید:
PROG=$(basename $0) VERSION='1.2' USAGE="A tiny example using process-getopt(1)" # برای تعریف تعدادی گزینه process-getopt فراخوانی توابع source process-getopt SLOT="" SLOT_func() { [ "${1:-""}" ] && SLOT="yes"; } # SLOTفراخوان برگشتی برای گزینه add_opt SLOT "boolean option" s "" slot TOKEN="" TOKEN_func() { [ "${1:-""}" ] && TOKEN="$2"; } # TOKEN فراخوانی برگشتی گزینه add_opt TOKEN "this option takes a value" t n token number add_std_opts # و غیره --help تعریف گزینههای استاندارد TEMP=$(call_getopt "$@") || exit 1 eval set -- "$TEMP" # getopt(1)درست مثل # حذف گزینه ها از سطر دستور process_opts "$@" || shift "$?" echo "SLOT=$SLOT" echo "TOKEN=$TOKEN" echo "args=$@"
اینجا برای بسیار آسانتر نمودن تألیف و نگهداری، تمام اطلاعات در مورد هر گزینه فقط در یک محل تعریف میشود.مقدار زیادی از چرکینی کار به طور خودکار زدوده میشود و از استانداردهای getopt(1) متابعت مینماید، زیرا getopt را برای شما فراخوانی میکند.
در حقیقت، آنچه نویسنده غفلت کرده که بگوید، آن است که به جای getopt، واقعاً از معناشناسی getopts استفاده میشود. من این تست را اجرا کردم:
~/process-getopt-1.6$ set -- one 'rm -rf /' 'foo;bar' "'" ~/process-getopt-1.6$ call_getopt "$@" -- 'rm -rf /' 'foo;bar' ''\'''
به نظر میرسد به اندازه کافی برای اداره گزینههای تهی، گزینههای شامل فضای سفید، و گزینههای شامل نقلقول منفرد، هوشمند باشد، به نحوی که باعث میگرددeval پیش روی شما متوقف نشود. اما این ظهرنویسی برای نرمافزار process-getopt به طور کلی نیست، من به اندازه کافی در بارهاش نمیدانم. -GreyCat
این برنامه نوشته شده و تست شده در لینوکس است، جایی که getopt(1) از گزینههای بلند پشتیبانی میکند. برای قابلیت انتقال، در حین اجرا getopt(1) محلی را تست میکند و اگریک نمونه غیر-گنو پیدا کند(یعنی نمونهای که برای getopt --test عدد 4 برنمیگرداند) فقط گزینههای کوتاه را پردازش میکند. از دستور داخلی bash به نام getopts(1) استفاده نمیکند. -bhepple
پرسش و پاسخ 35 (آخرین ویرایش 2012-10-22 20:41:23 توسط GreyCat)
مطمئناً!
i=1 sp="/-\|" echo -n ' ' while true do printf "\b${sp:i++%${#sp}:1}" done
در هر نوبت که حلقه تکرار میشود، کاراکتر بعدی در رشته sp را نمایش میدهد، هنگامیکه به انتها میرسد از سر گرفته میشود. (i موقعیت کاراکتر جاری برای نمایش است و ${#sp} طول رشته sp است).
رشته \b با یک کاراکتر 'backspace' تعویض میشود. به طور جایگزین، میتوانستید با \r به ابتدای سطر برگردید.
اگر میخواهید آهسته شود، از فرمان sleep در داخل حلقه(بعد از printf) استفاده کنید.
اگر از قبل حلقهای دارید که کارهای زیادی انجام میدهد، میتوانید تابع زیر را برای به روزرسانی چرخنده در ابتدای هر تکرار آن فراخوانی کنید:
sp="/-\|" sc=0 spin() { printf "\b${sp:sc++:1}" ((sc==${#sp})) && sc=0 } endspin() { printf "\r%s\n" "$@" } until work_done; do spin some_work ... done endspin
تکنیک مشابهی برای ساختن نوار پیشرفت میتواند به کار برود.
پرسش و پاسخ 34 (آخرین ویرایش 2009-12-30 18:39:39 &38206 توسط MatthiasPopp)
به طور معمول به پردازش با استفاده از شماره شناسایی پردازش(PID) رجوع میشود، و فرمان ps(1) میتواند اطلاعات هر پردازشی با شماره شناسایی (ID) داده شده را نمایش بدهد، به عنوان مثال
$ echo $$ # شماره شناسایی پردازش من 21796 $ ps -p 21796 PID TTY TIME CMD 21796 pts/5 00:00:00 ksh
اما خیلی اوقات شماره شناسایی یک پردازش معلوم نیست، بلکه فقط نام آن مشخص میباشد. بعضی سیستم عاملها، به عنوان مثال سولاریس، BSD، و برخی نگارشهای لینوکس، دارای یک دستور اختصاصی برای جستجوی پردازشی با نام معلوم میباشند، که pgrep(1) نامیده شده است:
$ pgrep init 1
حتی اغلب یک برنامه تخصصیتر نه تنها برای یافتن ID پردازش یک فرآیند با نام معین، بلکه همچنین برای ارسال سیگنال به آن پردازش، در دسترس میباشد:
$ pkill myprocess
بعضی سیستمها pidof(1) را نیز فراهم نمودهاند. این با pgrep در آن،شماره شناساییهای پردازش چندتاییِ خروجی، فقط با فاصله از هم جدامیشوند نه با سطر جدید، تفاوت دارد زیرنویس مترجم 1 .
$ pidof cron 5392
اگر این برنامهها در دسترس نیستند، کاربر میتواند خروجی فرمان ps را با استفاده از grep جستجو نماید.
مشکل عمده موقع استفاده از grep با خروجی فرمان ps آنست که grep ممکن است با مدخل ps خودش منطبق شود(این مورد را امتحان کنید: ps aux | grep init). برای وخیمتر شدن موضوع، این در هر نوبت اجرا اتفاق نمیافتد، نام فنی این مورد RaceCondition است. برای اجتناب از آن چند روش موجود است:
ps aux | grep name | grep -v grepتمام سطرهای شامل "grep" را از خروجی پاک میکند. اشکال: همیشه وضعیت خروج grep -v را خواهید داشت، بنابراین به عنوان مثال نمیتوانید وجود یک پردازش خاص را بررسی کنید.
ps aux | grep -v grep | grep nameاین دقیقاً همان کار را انجام میدهد، به استثنای آنکه وضعیت خروج "grep name" قابل دستیابی است و نشان دهنده «name پردازشی از ps هست» یا «name پردازشی از ps نیست» میباشد. هنوز اشکال شروع یک پردازش جدید(grep -v) را دارد.
ps aux | grep [n]ame
این مورد فقط پردازش grep مورد نیاز را تولید میکند. رمز آن در کاربرد کلاس کاراکتر [] است(عبارتهای منظم). قرار دادن تنها یک کاراکتر در گروه کاراکتری معمولاً به هیچ وجه معقول نیست، زیرا [c] همیشه با یک "c" تطبیق میکند. در این حالت، نیز همانطور است. grep [n]ame کلمه "name" را جستجو میکند. اما چون پردازش متعلق به خود grep مدخلی را لیست میکند که شما اجرا نمودهاید("grep [n]ame") و "grep name" نیست، با خودش منطبق نمیشود.
تمام موارد فوق موقعی صحیح است که شما در اعلان محاورهای پوسته باشید، اما نباید در اسکریپت استفاده شود. خیلی غیرقابل اطمینان است.
اکثر اوقات موقعی که شخصی چنین سؤالی میپرسد، به دلیل آنست که میخواهد با تکنیکهای ابتدایی اسکریپتنویسی یک daemon را مدیریت نماید. انواع همگانی این پرسشها عبارتند از: « چگونه میتوانم PID فلان پردازش خود را به دست آورم....به طوری که اگر آن پردازش از قبل در حال اجرا نباشد آن را شروع کنم» یا «چطور میتوانم PID بَهمان پردازش خود را به دست آورم... چون میخواهم در صورتیکه از قبل فعال شده است از اجرای آن توسط اسکریپت اجتناب نمایم.» این پرسشها هر دو به طور خطرناکی منجر به ایجاد ضعف سیستم میگردند.
اگر به راستی آنچه میخواهید، آن است که هر وقت daemon مورد نظرتان میمیرد، شروع مجدد بشود، فقط این کار را انجام بدهید:
while true; do mydaemon --in-the-foreground done
که در آن --in-the-foreground گزینهای است که اگر وجود داشته باشد، باید به daemon داده شود تا از بردن خودش به طور خودکار به پس زمینه اجتناب نماید. (اغلب، -d این کار را انجام میدهد و سودمندی بیشتری از اجرای daemon با تفصیلنویسی افزایش یافته را دارد.) برنامههایی کهdaemonها را در خود دارند ممکن است آماج بعدی لفاظی greycat باشند یا نباشند....
اگر این خیلی سادهلوحانه است، به daemontools یا runit که برنامههایی برای مدیریت سرویسها میباشند، نگاه کنید.
اگر واقعاً آنچه شما میخواهید پیشگیری از اجرای چند نمونه از برنامه خودتان است، آنوقت تنها روش مطمئن انجام آن، کاربرد یک قفل است. برای تفصیل بیشتر در مورد انجام این کار، بخش مدیریت پردازش یا پرسش و پاسخ شماره 45 را ببینید.
مدیریت پردازش، مباحثی مانند«من میخواهم گروه job خود را به 5 رشته تقسیم نموده و تمام آنها را به طور موازی اجرا نمایم.» را هم پوشش میدهد. لطفاً آن را بخوانید.
$pidof getty 1836 1083 1034 1025 1006 999 $pgrep getty 999 1006 1025 1034 1083 1836(1)
پرسش و پاسخ 33 (آخرین ویرایش 2009-12-30 18:39:04 توسط MatthiasPopp)
کلمه کلیدی time در پوسته Bash نیرنگ خاصی را به کار میبرد، به طوری که میتوانید مواردی مانند این را انجام دهید
time find ... | xargs ...
و به جای فقط فرمان ساده در ابتدای لوله، زمان اجرای تمام لوله را، به دست میآورد. (این به دلایل روشن با رفتار فرمان خارجی time(1) متفاوت میباشد.)
به این دلیل، کسانی که میخواهند خروجی time را تغییر مسیر بدهند غالباً در معین کردن آنکه انواع توصیفگرهای فایل به کجا میروند، با دشواری مواجه میگردند. گرچه آنطوری هم که اکثر افراد فکر میکنند دشوار نیست، --ترفند آن فراخوانی time در یک پوسته فرعی یا بلوک، و سپس گرفتن stderr پوسته فرعی یا بلوک میباشد(که شامل نتایج time است). اگر احتیاج دارید که خروجی یا خطای استاندارد واقعی فرمان را تغییر مسیر بدهید، آن را در داخل پوسته فرعی یا بلوک انجام دهید. برای نمونه:
bash -c "time ls" 2>time.output # صریح، اما بیکفایت ( time ls ) 2>time.output # به طور جزئی مؤثرتر { time ls; } 2>time.output # مؤثرترین # :حالت کلی { time some command >stdout 2>stderr; } 2>time.output
foo=$( bash -c "time ls" 2>&1 ) # هر چیزی را اخذ میکند foo=$( { time ls; } 2>&1 ) # نسخه کارآمدتر # میشود stdout مانع آزار رساندن exec 3>&1 foo=$( { time bar 1>&3; } 2>&1 ) # خطای استاندارد و زمان را اخذ میکند exec 3>&- # میشود stdout و stderr مانع مزاحمت هر دو خروجی exec 3>&1 4>&2 foo=$( { time bar 1>&3 2>&4; } 2>&1 ) # فقط زمان را اخد میکند exec 3>&- 4>&- # exec همانطور و بدون استفاده از { foo=$( { time bar 1>&3- 2>&4-; } 2>&1 ); } 3>&1 4>&2
ساختار مشابهی برای اخد پیغامهای "core dump" میتواند استفاده شود، که در حقیقت توسط پوستهای که برنامه را شروع کرده، چاپ میشوند، نه به وسیله همان برنامهای که فایل core آن از حافظه تهیه میشود:
./coredump >log 2>&1 # در گرفتن پیغام ناموفق است { ./coredump; } >log 2>&1 # پیغام را اخذ میکند
پرسش و پاسخ 32 (آخرین ویرایش 2012-04-20 04:39:57 توسط e36freak)