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

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

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

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

نوشتن مکرر در یک فایل fifo باز


چگونه می‌توان چند مرتبه در فایل fifo بدون باز کردن مجدد آن، نوشت؟

در یک حالت کلی، یک توصیف‌گرفایل (FD) جدید باز خواهید نمود که به fifo اشاره کند، و از طریق آن می‌نویسید. برای موقعیت‌های ساده، شاید پرش از این مرحله امکان پذیر باشد.

اصلی‌ترین روش، استفاده از لوله‌های با نام است:

mkfifo myfifo
cat < myfifo &
echo 'a' > myfifo

این کد کار می‌کند، اما cat بعد از خواندن یک سطر می‌میرد. (در حقیقت، آنچه رخ می‌دهد آن است که وقتی لوله با نام توسط تمام نویسنده‌ها بسته می‌شود، یک سیگنال وضعیت پایان فایل برای خواننده ارسال می‌شود. بنا بر این cat، یعنی خواننده، به دلیل آنکه یک پایان فایل در ورودی‌اش می‌بیند، خاتمه می‌یابد.)

اگر بخواهیم چندین مرتبه بدون داشتن شروع مجدد برای خواننده، در یک لوله بنویسیم چه؟ باید تمام داده‌های خود را طوری مرتب کنیم که بدون چندین مرتبه باز و بسته کردن لوله فرستاده بشوند.

اگر فرمانها متوالی باشند، می‌توانند گروه‌بندی بشوند:

cat < myfifo &
{ echo 'a'; echo 'b'; echo 'c'; } > myfifo

یک انتخاب می‌تواند، استفاده از ‎tail -f‎ به جای cat باشد:

tail -f myfifo &

echo 'a' > myfifo
# نمی‌میرد
echo 'b' > myfifo
echo 'c' > myfifo

در اینجا مشکل این است که، حتی اگر لوله با نام حذف شود، پردازش tail نمی‌میرد. در اسکریپت این مشکلی نیست چون می‌توانید tail را موقع خروج kill کنید. همچنین یک گزینه ‎ --pid‎ برای tail وجود دارد که در چنین حالاتی می‌تواند سودمند باشد.

اما اگر آن فرمانها به عللی نتوانند گروهی بشوند یا اگر استفاده از tail یک انتخاب نباشد، راه بهتر تخصیص یک توصیف‌گر فایل به لوله و نوشتن در آنجا می‌باشد:

cat < myfifo &

# را اختصاص می‌دهد ‎fd 3‎ توصیف‌گر‎
exec 3>myfifo

# به جای باز کردن مجدد لوله ‎fd 3‎ نوشتن در‎
echo 'a' >&3
echo 'b' >&3
echo 'c' >&3

#  fd  بستن‎
exec 3>&-

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

راه حل توصیف‌گر‌فایل فوق به خوبی برای نوشتن در یک fifo از یک پوسته منفرد، به خوبی کار می‌کند، اما در صورتی‌که پردازش‌های بسیاری نیاز به نوشتن در fifo داشته باشند، اینطور نیست. در این حالت، یک راه چاره باز نگاه داشتن fifo توسط یک نویسنده است:

mkfifo myfifo

# باز شده را برای استفاده یک نویسنده حفظ می‌کند fifo ‎
cat > myfifo

یک خواننده را در پوسته دیگری باز کنید:

cat myfifo

آنوقت هر تعداد پوسته که می‌خواهید برای ارسال داده‌ها به خواننده به کار ببرید:

echo something > myfifo
# خواننده نمی‌میرد‎

برای خاتمه یافتن خواننده، به اولین cat (نویسنده) بازگردید و با استفاده از ‎CTRL-C‎ یک EOF برایش ارسال کنید(یا با استفاده از ‎CTRL-C‎ آن را خاتمه بدهید).


CategoryShell

پرسش و پاسخ 85 (آخرین ویرایش ‎2013-03-07 17:00:39‎ توسط put92-6-88-165-39-120)


برگشت یک رشته یا نتیجه‌ای از یک تابع


چگونه یک رشته (یا عدد بلند، یا عدد منفی) را در نتیجه یک تابع برگشت بدهم؟ return فقط استفاده از اعداد 0 تا 255 را اجازه می‌دهد.

توابع در Bash (همچنین سایر پوسته‌های هم خانواده شل Bourne) مانند فرمانها کار می‌کنند: یعنی، آنها فقط یک وضعیت خروج برگشت می‌دهند که منحصر به اعداد صحیح از 0 تا 255 است. این به منظور استفاده در علامت‌دهی خطاها می‌باشد، نه برای بازگرداندن نتایج محاسبات یا سایر داده‌ها.

اگر نیاز دارید داده‌های اختیاری را از یک تابع به فراخواننده آن باز گردانید، حداقل سه شیوه وجود دارد که توسط آنها می‌توانید این نیاز را برآورده کنید:

  • ممکن است تابعی داشته باشید که داده را در stdout بنویسد، و سپس فراخوان کننده stdout را بگیرد.
    •   foo() {
           echo "this is my data"
        }
        x=$(foo)
        echo "foo returned '$x'"

    یک اشکال این روش آنست که تابع در یک پوسته فرعی اجرا می‌شود، و این به معنای آنست که هر تخصیص متغیر و غیره که در تابع انجام شده بر محیط فراخوان کننده تأثیر نمی‌گذارد (و به واسطه یک ‎fork()‎ متحمل خسارت سرعت هم می‌گردد). این مطلب بسته به احتیاجات برنامه و تابع شما، می‌تواند مشکلی باشد یا نباشد. یک اشکال دیگر آنست که هر آنچه توسط تابع foo چاپ می‌شود، اخذ می‌گردد و در متغیر قرار داده می‌شود. اگر تابع foo همچنین چیزهایی را که قرار نیست یک مقدار برگشتی باشند، می‌نویسد، این مطلب به مشکلاتی منجر می‌شود. برای جداکردن اعلانهای کاربر و(یا) پیغام خطاها از داده‌های برگشتی، آنها را به stderr که توسط فراخواننده اخذ نمی‌شود تغییر مسیر بدهید.

  •  foo() {
        echo "running foo()..."  >&2  #می‌فرستد stderr اعلانهای کاربر و خطاها را به 
        echo "this is my data"       # این مقدار پایین به متغیر اختصاص داده می‌شود
     }
     x=$(foo)                        # را چاپ می‌کند running foo()...
     echo "foo returned '$x'"        # را چاپ می‌کند foo returned 'this is my data'
  • می‌توانید داده را به متغیرهای سراسری تخصیص بدهید، و سپس در فرا خوان کننده به آن متغیرها ارجاع بدهید.
    •   foo() {
           return="this is my data"
        }
        foo
        echo "foo returned '$return'"

    اشکال این شیوه آنست که اگر تابع در پوسته فرعی اجرا بشود، آنوقت تخصیص به متغیر سراسری در تابع، توسط فراخواننده دیده نخواهد شد، این بدان معناست که به عنوان مثال، شما قادر به استفاده از تابع در یک خط لوله نخواهید بود.

  • تابع شما می‌تواند داده‌اش را در یک فایل بنویسد، در جایی که فراخواننده بتواند آن را بخواند.
    •   foo() {
           echo "this is my data" > "$1"
        }
        # !این کد استواری برای مدیریت فایلهای موقتی نیست
        tmpfile=$(mktemp)   # GNU/Linux
        foo "$tmpfile"
        echo "foo returned '$(<"$tmpfile")'"
        rm "$tmpfile"
        #  کرده بودیم trap اگر این برنامه واقعی بود، بررسی خطا و  
      • اشکالات این شیوه نیز باید واضح باشد: شما نیاز به مدیریت فایل موقت دارید، که همواره ناجور است، باید یک دایرکتوری قابل نوشتن در جایی، و فضای کافی برای نگهداری داده‌ها در آن وجود داشته باشد، و غیره. از جنبه مثبت، صرف نظر از اینکه تابع در پوسته فرعی اجرا می‌شود، کار خواهد کرد.

    برای اطلاعات بیشتر در مورد مدیریت فایلهای موقت از درون اسکریپت پوسته، پرسش و پاسخ شماره 62 را ببینید. برای trapها، SignalTrap را ملاحظه کنید.


CategoryShell

پرسش و پاسخ 84 (آخرین ویرایش ‎2012-10-16 17:56:51‎ توسط GreyCat)


Defined، Declared، و Undefined


چگونه می توانم تعیین کنم که آیا یک متغیر قبلاً تعریف شده است؟

چندین روش بر حسب موارد نیاز، برای بررسی یک متغیر تعریف شده یا غیرتهی یا تابع وجود دارد.

"Declared"، "Defined"، و "Undefined"

همانند بسیاری از زبانها تعیین و تعریف می‌توانند مراحل مستقلی باشند. تعیین(declare) یک شیء، دادن نام و نوع داده به آن است، در جایی که تعریف(definition) آن را با یک مقدار مرتبط می‌کند، که می‌تواند تهی باشد. در واژگان فنی پوسته یونیکس، set، unset، و null به ترتیب به معنی تعریف نشده، تعریف شده، و تهی می‌باشند. چون رشته تقریباً تنها نوع‌داده رایج است، "null" معمولاً به معنی رشته تهی است. در مورد آرایه‌ها، موارد سازگاری بین پوسته‌ها کمتر می‌شود.

بررسی برای پارامتر/متغیر تعریف شده غیرتهی.

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

test x"$var" != x

فقط برای پوسته‌های باستانی Bourne. در اسکریپت‌های مدرن از این استفاده نکنید.

test -n "$var"

‎POSIX sh به اضافه پوسته‌های قدیمی‌تر Bourne که فاقد ‎[‎ هستند.

[ -n "$var" ]

‎POSIX sh‎ و اکثر پوسته‌های Bourne. روی‌هم رفته بهترین انتخاب برای اسکریپت‌های جدید نیازمند سازگاری با POSIX.

test "$var"

‎POSIX sh‎، استاندارد POSIX تصریح می‌کند که غیرتهی همیشه صحیح است در حالیکه تهی همیشه غلط است.

[ "$var" ]

‎POSIX sh‎، مانند مورد فوق بازهم خیلی قابل حمل.

[[ -n $var ]]

این مورد و بعدی فقط در ‎Bash/Ksh/Zsh‎، این با برخی نگارشهای پوسته buggy که گزینه ‎-n‎ صریح را به طور صحیح پردازش نمی‌کنند سازگار است.

[[ $var ]]

به طور کلی در جایی که POSIX مورد نیاز نیست بهترین انتخاب برای Bash و Ksh. به طور معمول دارای بهترین کارایی. در Zsh کار نخواهد کرد.

(( ${#var} ))

تقریباً به اندازه مورد قبل قابل حمل. در واقع همیشه آهسته‌تر. از این مورد برای بررسی رشته‌های تهی استفاده نکنید.

برای بررسی معکوس، آیا یک متغیر تهی است(unset یا به طول صفر)، منطق بالا را برعکس کنید.

test x"$var" = x
test -z "$var"
[ -z "$var" ]
test ! "$var"
[ ! "$var" ]
[[ -z $var ]]
[[ ! $var ]] #      یا هر مورد تقلید شده آن کار نخواهد نمود. اگر چنین Zsh همچنین در 
             #‎‎‏ در این صفحه را تنظیم کنید‏-z‎ یا ‎-n قابلیتی مورد نیاز است، راه حل صریح

بررسی آن که یک متغیر تعیین شده است

تمیز دادن میان متغیری که تعریف‌نشده است و آنکه تعریف‌شده اما تهی است تا اندازه‌ای ماهرانه، اما امکان‌پذیر است.

# POSIX
${var+:} false
! ${var+false}
[ -n "${var+_}" ]
[ "${var+_}" ]

اینکه کدام یک از موارد فوق بهترین اجراست از یک پوسته تا دیگری فرق می‌کند. اگر POSIX مورد نیاز نیست، راه حل ‎[[‎ پایین معمولاً بهترین اجرا می‌باشد.

# Bash/ksh
[[ ${var+_} ]]

شیوه دیگری که گهگاهی دیده شده است، بررسی وضعیت ‎typeset -p‎ است. روش فوق باید بر آن ترجیح داده شود.

# Bash/ksh/zsh
typeset -p var >/dev/null 2>&1
#  موجود باشد صفر را و در غیر آن صورت خطا برمی‌گرداندvar اگر

‎Bash 4.2‎ یک ‎-v test اضافه می‌کند:

# Bash 4.2 / ksh93
if [[ -v var ]]; then echo "var is defined"; fi

ksh93 به طور افزون از شاخص‌های آرایه با ‎-v test‎ پشتیبانی می‌کند، در حالیکه Bash تا به حال پشتیبانی نمی‌کند. وقتی که مقصد به طور خاص ksh93 باشد، باید این شیوه ترجیح داده شود.

بررسی آنکه یک تابع تعریف شده است

برای تعیین آنکه آیا تابعی با نام معین قبلاً تعریف شده است، چند جواب وجود دارد، تمام آنها به فرمانهای Bash (یا حداقل پوسته‌های غیرBourne) نیاز دارند. بررسی آنکه تابعی از قبل تعریف شده است، به ندرت لازم خواهد شد.

typeset -f f >/dev/null        #   Bash/Ksh -    تعریف نشده باشد غلط در غیر آنصورت‎
                               #                                  .صحیح برمی‌گرداند
[[ $(typeset -f f) ]]          #   Bash/Ksh -  در صورتیکه تعریف نشده باشد چیزی چاپ‎
                               # نمی‌کند در غیر آنصورت تعریف تابع را در خروجی می‌دهد
[[ $(type -t f) == function ]] #   اگر تعریف شده باشد تابع را بیرون می‌دهد Bash فقط‎
 #                  است "whence -v" مستعاری برای "type"  فرق می‌کند،(mksh و) ksh در‎

 #    راه عبوری برای مورد فوق دارند اما دو روش اول بیشتر قابل ترجیح هستند Bash/Ksh 
isFunction() [[ $(type ${BASH_VERSION:+-t} "$1") == ${KSH_VERSION:+"$1 is a "}function ]]; isFunction f 

پرسش و پاسخ 83 (آخرین ویرایش ‎2012-11-23 09:57:53‎ توسط ormaaj)


ارجحیت ترکیب ‎$(...)‎‎ نسبت به `...`


چرا‎ $(...) ‎نسبت به ‎ `...` ‎  (نقل‌قول برعکس) ارجحیت دارد؟

`...` ترکیب دستوری موروثی است که فقط در پوسته‌های خیلی قدیمی ناسازگار با POSIX لازم می‌شود. چندین دلیل برای ترجیح دادن همیشگی ترکیب ‎$(...)‎ وجود دارد:

تفاوتهای مهم

  • داخل نقل‌قولهای وارونه(backticks) با ممیزهای برعکس ‎(\)‎به یک حالت نا معلوم رفتار می‌شود:
    •   $ echo "`echo \\a`" "$(echo \\a)"
        a \a
        $ echo "`echo \\\\a`" "$(echo \\\\a)"
        \a \\a
        # Note that this is true for *single quotes* too!
        $ foo=`echo '\\'`; bar=$(echo '\\'); echo "foo is $foo, bar is $bar" 
        foo is \, bar is \\
  • نقل‌قول‌های تو در تو داخل ‎$()‎ خیلی بیشتر مناسب هستند.

    •   echo "x is $(sed ... <<<"$y")"

      در این مثال، با نقل‌قول‌های اطراف ‎$y‎ به عنوان یک زوج رفتار می‌شود، به علت آن که آنها داخل ‎$()‎ هستند. این در نگاه اول مغشوش به نظر می‌آید، زیرا اکثر برنامه‌نویسان C انتظار دارند با نقل‌قول قبل از x و نقل‌قول قبل از ‎$y‎ به عنوان یک زوج رفتار بشود، اما در پوسته‌ها این درست نیست. از طرف دیگر کد زیر،

        echo "x is `sed ... <<<\"$y\"`"
      برای قابل حمل شدن نیاز به \ها در اطراف نقل‌قول‌های داخلی دارد، پوسته‌های Bourne و Korn به این \ها نیاز دارند، در حالیکه Bash و dash لازم ندارند.
  • جایگزینی‌های فرمان تو در تو را آسانتر می‌کند. مقایسه کنید:
    •   x=$(grep "$(dirname "$path")" file)
        x=`grep "\`dirname "$path"\`" file`

      بعد از دومرحله زشت‌تر و بدقواره‌تر می‌شود. ‎$()‎ زمینه کاملاً جدید نقل‌قولی را تحمیل می‌کند، به طوری که هر چیز درون جایگزینی فرمان محافظت می‌شود و می‌تواند مثل اینکه خودش به تنهایی بوده، بدون ارتباط خاص با نقل‌قول و escaping، با آن رفتار بشود.

سایر مزایا

  • تابع ‎$(...)‎ به عنوان یک بسط از لحاظ بصری واضح است. ترکیب نشانه پیشوندی $ سازگار با تمام بسط های دیگر است، که در همان زمان از چپ به راست از درون نقل‌قولهای دوگانه تفکیک می‌شوند. فقط نقل‌قولهای وارونه(backticks) مستثنی می‌باشند. این مطلب قابلیت خواندن ماشین و انسان را بهبود می‌دهد، و ترکیب دستوری نامتناقض، زبان را برای خوانندگان محسوس‌تر می‌کند.

  • مطابق بالا، اشخاص(امیدواریم)با بسط‌های نقل‌قولی دوگانه و بسط‌های جایگزینی با ترکیب دستوری معمول ‎"$..."‎ عادت داده می‌شوند. تقریباً همیشه نقل‌قولی کردن جایگزینی فرمان کار صحیحی می‌باشد، بازهم اکثریت عظیمی از نمونه‌های ‎`...`‎ پراکنده نقل‌قولی نشده پیدا می‌کنیم، شاید به دلیل اینکه آنهایی هنوز از دستور زبان موروثی استفاده می‌کنند که کمتر ورزیده هستند، یا به سبب ترکیب دستوری متفاوت آن را با سایر بسط‌ها مربوط نمی‌کنند.
    علاوه بر این، کاراکتر ‎`‎ موقعی که در مجاورت " قرار گیرد به سادگی استتار می‌شود، که حتی برای خواندن دشوارتر می‌گردد، مخصوصاً با فونت‌های کوچک یا غیر عادی.

  • backtickها همچنین به آسانی با نقل‌قول‌های منفرد اشتباه می‌شوند.

See also:


پرسش و پاسخ 82 (آخرین ویرایش ‎2013-01-24 10:53:38‎ توسط ormaaj)


کنترل وجود یک فرمان در مسیر PATH


چگونه می‌توانم تعیین کنم که یک فرمان آیا در جایی از PATH من وجود دارد؟

POSIX فرمان داخلی به نام command تعیین می‌کند، که می‌تواند برای این منظور به کار برود:

# POSIX
if command -v qwerty >/dev/null; then
  echo qwerty exists
else
  echo qwerty does not exist
fi

در BASH، یک زوج دستور داخلی بیشتر نیز وجود دارد که ممکن است به کار بروند: hash و type. این هم مثالی با استفاده از hash:

# Bash
if hash qwerty 2>/dev/null; then
  echo qwerty exists
else
  echo qwerty does not exist
fi

یا، اگر ترجیح می‌دهید type:

# Bash
# .بدون در نظر گرفتن دستورات داخلی و غیره می‌کند PATH الزام به جستجوی ‎type -P‎ دستور
if type -P qwerty >/dev/null; then
  echo qwerty exists
else
  echo qwerty does not exist
fi

پوسته Korn در عوض دارای whence می‌باشد:

# ksh
if whence -p qwerty >/dev/null; then
  echo qwerty exists
else
  echo qwerty does not exist
fi

دستور داخلی command همچنین برای دستورات داخلی پوسته صحیح را باز می‌گرداند(برخلاف ‎type -P‎). اگر شما باید به طور مطلق فقط PATH را بررسی کنید، تنها روش POSIX انجام تکرار روی آن است:

# POSIX
IsInPath ()
(
  [ $# -eq 1 ] && [ "$1" ] || return 2
  set -f; IFS=:
  for dir in $PATH; do
    [ -z "$dir" ] && dir=.       # رفتار موروثی
    [ -x "$dir/$1" ] && return
  done
  return 1
)

if IsInPath qwerty; then
  echo qwerty exists
else
  echo qwerty does not exist
fi

توجه نمایید که تابع تعریف شده فوق به جای ابروهای معمول، از پرانتزها در اطراف بدنه استفاده می‌کند. این باعث می‌شود بدنه در یک پوسته فرعی اجرا گردد، و این به دلیل آنست که ما می‌خواهیم نیازی به خنثی نمودن ‎set -f‎ یا IFS نداشته باشیم.

رویکرد تکرار کننده در اسکریپت‌های configure نیز استفاده می‌شود. این هم نگارش ساده شده‌ای از یک چنین تستی:

# Bourne
save_IFS=$IFS
IFS=:
found=no
for dir in $PATH; do
  if test -x "$dir/qwerty"; then
    echo "qwerty is installed (in $dir)"
    found=yes
    break
  fi
done
IFS=$save_IFS
if test $found = no; then
  echo "qwerty is not installed"
fi

اسکریپت‌های واقعی configure معمولاً خیلی بیشتر از این پیچیده هستند، چون آنان ممکن است با سیستم‌هایی سر و کار داشته باشند که در آنها ‎$PATH‎ به وسیله کاراکترهای کولن جدا نمی‌شوند، یا سیستم‌هایی که در آنها شاید برنامه‌های اجرایی پسوندهای انتخابی مانند ‎.EXE‎ دارند، یا متغیرهای ‎$PATH‎ که دایرکتوری کاری جاری به عنوان یک رشته تهی در آنها گنجانده شده‌اند و غیره. اگر به چنین مطالبی علاقمند هستید، پیشنهاد می‌کنم یک اسکریپت ‎configure‎ واقعی تولید شده با autoconf گنو را بخوانید. آنها بیش از آن بلند و پیچیده می‌باشند که در این پرسش و پاسخ قرار داده شوند.

فرمان which (که غالباً یک اسکریپت csh است، اگر چه گاهی نیز به صورت باینری کامپایل شده است) برای این منظور قابل اطمینان نیست. which نمی‌تواند یک کد خروج سودمند تنظیم کند، و حتی نمی‌تواند خطاها را در stderr بنویسد. بنابر این، برای مطلع شدن از اجرای موفقیت آمیز آن، باید خروجی‌اش تفکیک بشود(هر جا که خروجی نوشته بشود).

# Bourne.  Last resort -- using which(1)
tmpval=`LC_ALL=C which qwerty 2>&1`
if test $? -ne 0; then
  # این ماشین یک کد خروج غیرصفر ‎which(1)‎ در حال حاضر فرض خواهیم نمود که اگر ‎
  # تنظیم کند، به راستی ناموفق  بوده. هنوز باید حالتی را ببینیم که در آن‎
  #  درست  مثل موفقیت غلط -- یک عدم موفقیت غلط تنظیم می‌کند which(1)‎
  echo "qwerty is not installed.  Please install it."

else
    #  صفر را بازگردانده، که به معنی موفقیت آن نیست. جستجو برای رشته های خطا
    case "$tmpval" in
      *no\ *\ in\ *|*not\ found*|'')
        echo "qwerty is not installed.  Please install it."
        ;;
      *)
        echo "Congratulations -- it seems you have qwerty (in $tmpval)."
        ;;
    esac
fi

توجه نمایید که خروجی ‎which(1)‎ موقعی که دستوری را پیدا نمی‌کند در تمام سکوها یکسان نیست. برای مثال، در ‎HP-UX 10.20‎، این پیغام خطا را چاپ می‌کند ‎no qwerty in /path /path /path ...‎، در ‎OpenBSD 4.1‎، این پیغام خطا را ‎qwerty: Command not found.‎، در دبیان (حداقل ‎3.1‎ تا ‎5.0‎) و SuSE، ابداً چیزی چاپ نمی‌کند، در ‎Red Hat 5.2‎، چاپ می‌کند ‎which: no qwerty in (/path:/path:...)‎، در ‎Red Hat 6.2‎، همان پیغام را می‌نویسد، اما به جای خروجی استاندارد در خطای استاندارد، و در Gentoo، چیزی در stderr می‌نویسد.

ما قویاً پیشنهاد می‌کنیم از which استفاده نکنید. به جای آن یکی از دستورات داخلی یا رویکردهای تکرار را به کار ببرید.


پرسش و پاسخ 81 (آخرین ویرایش ‎2012-03-14 11:57:13‎ توسط pgas)