آموزش‌های خط فرمانی

این وبلاگ تلاش می‌کند گامی در حد بضاعت در جهت آموزش خط فرمان و اسکریپت‌نویسی پوسته گنو-لینوکس بردارد.

آموزش‌های خط فرمانی

این وبلاگ تلاش می‌کند گامی در حد بضاعت در جهت آموزش خط فرمان و اسکریپت‌نویسی پوسته گنو-لینوکس بردارد.

فصل مشترک یا تفاوت دو فایل


چطور می‌توانم تمام سطرهایی را به دست آورم که: در هر دو فایل هستند(فصل مشترک) یا فقط در یکی از دوفایل می‌باشند(تفاوتها).

از فرمان‎ comm(1)‎ استفاده کنید:

# Bash
#  file2 و file1 فصل مشترک‎
# (یعنی فقط سطرهایی که در هر دو فایل وجود دارد)‎
comm -12 <(sort file1) <(sort file2)

#  file2 از file1 تفریق ‎
# (وجود دارد file2 یعنی سطرهایی که منحصراً در)‎
comm -13 <(sort file1) <(sort file2)

برای تفصیل بیشتر صفحات man دستور comm را بخوانید. آنها که آن بالا می‌بینید جایگزینی پردازش‌ها هستند.

اگر بنا به دلایلی سیستم شما فاقد برنامه مرکزی comm است، می‌توانید از این روشهای دیگر استفاده کنید. (در حقیقت، شما از هیچ کدام اینها واقعاً نباید استفاده کنید. این روشها توسط افرادی نوشته شده‌اند که هنوز چیزی در باره comm نمی‌دانستند. اما پیشنهادات محرمانه برای دوستداران کُندی!)

  1. یک پیاده‌سازی ساده و سریع شگفت آور، فقط در 20 ثانیه مطابقت فایل به اندازه‎ 30k ‎با فایلی با سطرهایی به اندازه ‎ 400k‎ را برای من انجام داد.
      #  file2 و file1 فصل مشترک‎
      grep -xF -f file1 file2
    
      #  file2 از file1 تفریق‎
      grep -vxF -f file1 file2
    • grep یکی از مجموعه‌ها را به عنوان لیست الگو از فایل‎ (-f)‎ می‌خواند، و الگوها را به عنوان یک رشته ساده نه عبارتهای منظم‎ (-F)‎ تفسیر می‌کند، مطابقت فقط با کل سطر‎ (-x)‎ انجام می‌شود.
    • توجه نمایید که فایل مشخص شده با ‎ -f‎ در حافظه بارگذاری خواهد شد، بنابراین برای فایلهای خیلی بزرگ معیار نیست.
    • این کُد با هر grep سازگار با POSIX کار خواهد کرد، در سیستم‌های قدیمی‌تر ممکن است به جای ‎grep -F‎ نیاز به استفاده از fgrep داشته باشید.

  2. یک پیاده‌سازی با استفاده از sort و uniq:
      #  file2 و file1 فصل مشترک‎
      sort file1 file2 | uniq -d  # (با این فرض که فایلها محتویات تکراری ندارند)‎
    
      # file1-file2 (تفریق)
      sort file1 file2 file2 | uniq -u
    
      # file2 - file1 (تفریق به همان طریق اما برعکس)‎
      sort file1 file2 file1 | uniq -u
  3. یک پیاده سازی دیگر برای تفریق:
      sort file1 file1 file2 | uniq -c |
      awk '{ if ($1 == 2) { $1 = ""; print; } }'
    • این شاید یک فاصله اضافی در ابتدای سطر وارد کند، اگر این مسئله است، آن فاصله را حذف کنید.
    • همچنین، این رویکرد فرض می‌کند که نه file1 و نه file2 سطر تکراری ندارند.
    • سرانجام، این خروجی را برای شما مرتب می‌کند. اگر این مسئله است، آنوقت شما به طورکلی این رویکرد را رها کنید. شاید بتوانید از آرایه‌های انجمنی awk به جای آن استفاده کنید(یا از hashهای پرل یا آرایه‌های tcl).
  4. این مثالها تفریق و فصل مشترک با awk هستند، صرف نظر از اینکه فایلهای ورودی مرتب شده یا دارای محتویات تکراری باشند:
      #   .نیستند را چاپ می‌کند 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 را ببینید.

See Also

http://www.pixelbeat.org/cmdline.html#sets


CategoryShell

پرسش و پاسخ 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"‎ شاید درموقع ورود به حلقه برقرار نباشد. چهار راه حل برای این موضوع وجود دارد:

  1. عدم استفاده از ‎ -u‎.

  2. تعویض ‎ case $1 in‎ با ‎case ${1+$1} in‎ (تمام کُدهای پس از آن را که ‎ set -u‎ نقض می‌کند، به خوبی توانمند می‌سازد).

  3. تعویض ‎ case $1 in‎ با‎ case ${1-} in‎ (هر متغیری که امکان اعلان نشدن دارد، برای ممانعت از اثر ‎ set -u‎، می‌تواند به صورت‎ ${variable-}‎ نوشته بشود).

getopts

هرگز از ‎ 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 عبارتند از:

  1. قابل حمل است، و به عنوان مثال در dash نیز کار می‌کند.
  2. مواردی مانند ‎ -vf filename‎ که روش یونیکسی مورد انتظار می‌باشد را به طور خودکار مدیریت می‌کند.

اشکال getopts آن است که فقط گزینه‌های کوتاه را مدیریت می‌کند(‎-h‎، نه ‎--help‎).

یک آموزش getopts وجود دارد که شرح می‌دهد هر یک از ترکیبات دستوری و متغیرها به چه معنی هستند. در bash، همچنین‎ help getopts‎ نیز وجود دارد، که می‌تواند آموزنده باشد.

همچنین بازهم در فراخوانی getopts آن اشکال وجود دارد که گزینه‌ها در حداقل دو، یا شاید سه مکان کُد می‌شوند، در جمله case که آنها را پردازش می‌کند و احتمالاً درپیغام راهنمایی که یکی از این روزها خواهان نوشتن آن می‌شوید. این یک فرصت کلاسیک برای رسوخ خطاها در کُدهای نوشته و نگهداری شده -غالباً هنوز زیاد توسعه نیافته، یا خیلی قدیمی‌تر- است. این مورد با استفاده از توابع فراخوانی برگشتی، می‌تواند پیش‌گیری بشود، اما این رویکرد نوعی خنثی نمودن هدف استفاده از getopts می‌باشد.

1. گزینه بلند ماهرانه getops

این هم یک مثال که مدعی تجزیه گزینه‌های بلند با 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 است.

پیمایش ساده تکراری brute-force زیرنویس ۱

یک رویکرد دیگر کنترل گزینه‌ها در دستورات 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 

و این در صورتی کار می‌کند که اسکریپت به این شکل اجرا بشود:

  • ./script --quick
  • ./script -other --quick

اما بدون "-" یا با "--" در اولین شناسه متوقف می‌شود:

  • ./script -bar foo --quick
  • ./script -bar -- --quick

البته، این رویکرد(تکرار روی لیست شناسه در هر نوبتی که می‌خواهید یک مورد را کنترل کنید،) نسبت به فقط یکبار تکرار و تنظیم نشانه متغیرها، خیلی کمتر کارامد است.

همچنین گزینه‌ها را در سرتاسر برنامه پخش می‌کند. گزینه لفظی ‎--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


  1. مترجم: brute-force یک سَبکِ برنامه‌نویسی است که در آن برنامه نویس به جای استفاده از هوشمندی خود برای ساده‌سازی مسئله، به قدرت پردازشی سیستم تکیه می‌کند. می‌توان آن را برنامه‌نویسی زمخت و بی‌ملاحظه نیز نامید (1)

CategoryShell

پرسش و پاسخ 35 (آخرین ویرایش ‎ 2012-10-22 20:41:23 ‎ توسط GreyCat)


اجرای چرخنده در bash


آیا می‌توانم در Bash یک چرخنده اجرا کنم؟

مطمئناً!

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

تکنیک مشابهی برای ساختن نوار پیشرفت می‌تواند به کار برود.


CategoryShell

پرسش و پاسخ 34 (آخرین ویرایش ‎ 2009-12-30 18:39:39 &38206 توسط MatthiasPopp)


به دست آوردن ID پردازش



چگونه می‌توانم شماره شناسایی پردازشی را با دادن نام آن پردازش به دست آورم؟

به طور معمول به پردازش با استفاده از شماره شناسایی پردازش(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 است. برای اجتناب از آن چند روش موجود است:

  • استفاده از ‎ grep -v ‎   در انتها
         ps aux | grep name | grep -v grep
    تمام سطرهای شامل "grep" را از خروجی پاک می‌کند. اشکال: همیشه وضعیت خروج ‎ grep -v‎ را خواهید داشت، بنابراین به عنوان مثال نمی‌توانید وجود یک پردازش خاص را بررسی کنید.
  • استفاده از ‎ grep -v‎ در وسط
         ps aux | grep -v grep | grep name
    این دقیقاً همان کار را انجام می‌دهد، به استثنای آنکه وضعیت خروج "grep name" قابل دستیابی است و نشان دهنده «name پردازشی از ps هست» یا «name پردازشی از ps نیست» می‌باشد. هنوز اشکال شروع یک پردازش جدید‎(grep -v)‎ را دارد.
  • استفاده از ‎ []‎ در grep
         ps aux | grep [n]ame

    این مورد فقط پردازش grep مورد نیاز را تولید می‌کند. رمز آن در کاربرد کلاس کاراکتر ‎ []‎ است(عبارتهای منظم). قرار دادن تنها یک کاراکتر در گروه کاراکتری معمولاً به هیچ وجه معقول نیست، زیرا ‎ [c]‎ همیشه با یک "c" تطبیق می‌کند. در این حالت، نیز همانطور است. ‎grep [n]ame‎ کلمه "name" را جستجو می‌کند. اما چون پردازش متعلق به خود grep مدخلی را لیست می‌کند که شما اجرا نموده‌اید(‎"grep [n]ame"‎) و "grep name" نیست، با خودش منطبق نمی‌شود.

لفاظی greycat: مدیریت 2daemon

تمام موارد فوق موقعی صحیح است که شما در اعلان محاوره‌ای پوسته باشید، اما نباید در اسکریپت استفاده شود. خیلی غیرقابل اطمینان است.

اکثر اوقات موقعی که شخصی چنین سؤالی می‌پرسد، به دلیل آنست که می‌خواهد با تکنیک‌های ابتدایی اسکریپت‌نویسی یک 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 رشته تقسیم نموده و تمام آنها را به طور موازی اجرا نمایم.» را هم پوشش می‌دهد. لطفاً آن را بخوانید.


  1. مترجم: یک مثال مناسب برای مشاهده تفاوت خروجی دو دستور pgrep و pidof

       $pidof getty
       1836 1083 1034 1025 1006 999
    
       $pgrep getty
       999
       1006
       1025
       1034
       1083
       1836
    
    (1)
  2. مترجم: برنامه‌های کمکی که معمولاً به طور صریح فراخوانی نمی‌شوند، اما به حالت خاموش برای وقوع شرط یا شرایطی در انتظار می‌مانند، مانند سرویس چاپ که تا رسیدن یک درخواست چاپ در انتظار می‌ماند. (2)

CategoryShell

پرسش و پاسخ 33 (آخرین ویرایش ‎ 2009-12-30 18:39:04 ‎ توسط MatthiasPopp)


پرسش و پاسخ شماره ۳۲



چطور می‌توانم خروجی 'time' را به یک متغیر یا فایل تغییر مسیر بدهم؟

کلمه کلیدی 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      # پیغام را اخذ می‌کند


CategoryShell

پرسش و پاسخ 32 (آخرین ویرایش ‎ 2012-04-20 04:39:57 ‎ توسط e36freak)