تعدادی روش اختیاری وجود دارد، اکثر آنها اختصاصی سیستم هستند. همچنین دقیقاً بستگی به آن دارند که چرا اطلاعات را میخواهید، در اکثر حالتها، روشهای دیگری برای انجام هدف واقعی شما وجود دارد. شما نیاز ندارید خروجی ls را تفکیک نمایید در صورتیکه برای اجتناب از انجام آن، یک روش امکان پذیر موجود باشد.
بسیاری از موقعیتهایی که در آنها شاید شما جویای مجوزها بشوید -- از قبیل من میخواهم هر فایلی که بیت setuid زیرنویس 1 آن تنظیم شده را پیدا کنم -- میتوانند با فرمان find(1) مدیریت بشوند.
برای برخی پرسشها، از قبیل میخواهم مطمئن شوم این فایل مجوز 0644 دارد، حقیقتاً شما نیاز به بررسی آن که مجوزها کدامند، ندارید. شما میتوانید فقط از chmod 0644 myfile استفاده کنید و آنها را به طور مستقیم تنظیم کنید. و اگر واقعا به جای مجبور کردن آنها به مجوز مورد نظر، نیاز به بررسی مجوزها دارید، آنوقت میتوانید از گزینه -perm فرمان find استفاده کنید.
اگر میخواهید ببینید آیا میتوانید فایلی را بخوانید، بنویسید، یا اجرا کنید، test -r , -x , -w موجود میباشند.
اگر میخواهید ببینید آیا اندازه یک فایل صفر است یا خیر، نیازی به خواندن اندازه فایل در یک متغیر ندارید. به جای آن میتوانید فقط test -s را به کار ببرید.
اگر میخواهید زمان ویرایش یک فایل را به دیگری کپی کنید، میتوانید touch -r را به کار ببرید. فرمان chown در بعضی سیستمهای گنو-لینوکس دارای گزینه --reference میباشد که به همان طریق کار میکند، به شما اجازه میدهد مالک، و گروه را از یک فایل به دیگری کپی نمایید.
اگر نیازهای شما مطابق با یکی از آنها نیست، و واقعاً احساس میکنید باید فوق دادههای یک فایل را استخراج و در یک متغیر قرار بدهید، آنوقت ما میتوانیم چند جایگزین را در نظر بگیریم:
در سیستمهای گنو-لینوکس، *BSD و احتمالاً دیگران، فرمانی به نام stat(1) وجود دارد. در سیستمهای قدیمیتر گنو-لینوکس، این فرمان گزینهای نمیگیرد -- فقط نام فایل -- و شما باید خروجی آن را تفکیک نمایید.
$ stat / File: "/" Size: 1024 Filetype: Directory Mode: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Device: 8,0 Inode: 2 Links: 25 Access: Wed Oct 17 14:58:02 2007(00000.00:00:01) Modify: Wed Feb 28 15:42:14 2007(00230.22:15:49) Change: Wed Feb 28 15:42:14 2007(00230.22:15:49)
در این وضعیت، شخص میتوانست 0755 را از سطر Mode:، با استفاده از awk یا فرمانهای مشابهی استخراج کند.
در سیستمهای گنو-لینوکس جدیدتر:
$ stat -c %a / 755
به طور آشکاری برای تجزیه خیلی آسانتر است. ترکیب دستوری با BSDها (NetBSD و OpenBSD و FreeBSD و مشتقات آنها مانند Apple OS/X)، متفاوت است و لازم است شما مجوزها را از روی mode استخراج کنید:
mode=$(stat -f %p -- "$filename") perm=$(printf %o "$((mode & 07777))")
perl -e 'printf "%o\n", 07777 & (stat $ARGV[0])[2]' "$filename"
این کد همان رشته اُکتال مثال stat -c %a را باز میگرداند، اما خیلی بیشتر قابل حمل است. (و آهستهتر).
فرمان find گنو دارای یک گزینه -printf است که میتواند هر فوقدادهای از یک فایل را به خروجی ارسال کند:
find "$filename" -prune -printf '%m\n'فرمان find بیش از یک دهه قدیمیتر از stat گنو است و همچنین میتواند فوقداده چندین فایل در یک دایرکتوری را ارائه کند. به هر حال مراقب باشید که برای فایلی به نام -print و ( و !... یا هر گزاره دیگر find، لازم است اطمینان حاصل کنید که نام فایل به صورت ./-print, ./(, ! یا سایر مسیرهای نسبی یا مطلقِ فایل عبور داده شود، که find سر در گم نگردد.
اگر bash شما با loadable builtin support کامپایل گردیده، میتوانید یک فایل finfo داخلی بسازید(در شاخه examples/loadables/ دایرکتوری فرعی درخت فایلهای منبع bash خودتان، تایپ کنید make)، آنرا enable کنید، و سپس کد زیر را به کار ببرید:
$ finfo -o .bashrc 644
آگاه باشید که finfo.c توزیع شده با bash تا نگارش 4.0 حداقل شامل یک باگ است(درگزینه -s )، بنابراین آشکارا کد زیاد بررسی نگردیده است. اکثر بستههای از پیش کامپایل شده bash شامل مثالهای کامپایل شده نیستند، از این جهت شاید این حایگزین برای اکثر کاربران دشوار باشد.
پرسش و پاسخ 87 (آخرین ویرایش 2013-07-25 13:37:13 توسط StephaneChazelas)
گاهی اوقات صرف نظر کردن از مستعارها (و توابع، از جمله توابع داخلی پوسته) سودمند است. به عنوان مثال، شاید در سیستم خود این تنظیم را داشته باشید:
alias grep='grep --color=auto'
اما گاهی اوقات، شما به انجام یک فرمان یک سطری به طرف لولهها نیاز دارید که در آنجا رنگها آشفتگی ایجاد میکنند. هر یک از موارد زیر را میتونید استفاده کنید:
unalias grep; grep ... #1 unalias -a; grep ... #2 "grep" ... #3 \grep ... #4 command grep ... #5
#1 قبل از استفاده از grep مستعار آن را عزل میکند، اگر grep مستعار نشده باشد، کاری انجام نمیدهد. به هر حال مستعار برای بقیه نشست آن پوسته نیز از بین رفته است.
#2 مشابه قبلی، اما تمام مستعارها حذف میشوند.
#3 و #4 یکسان هستند، به شما اجازه میدهند یکبار grep را در حالیکه از مستعار آن صرفنظر شده اجرا کنید، اما به طور کلی خیر.
#5 متمایز از دیگران است، در آن از مستعارها، توابع، و کلیدواژههای پوسته از قبیل time صرفنظر میشود. بازهم دستورات داخلی مانند echo نسبت به /bin/echo ترجیح داده میشوند. چند گزینه دارد که شاید بخواهید استفاده کنید -- help command را ببینید.
انتخاب #6 نوشتن تابع خودتان خواهد بود که وقتی ترمینال خروجی استاندارد نباشد رفتار نامطلوب مرتکب نمیشود. به این ترتیب:
ls() { if test -t 1; then command ls -FC "$@" else command ls "$@" fi }
استفاده از این تابع به جای alias ls='ls -FC' موقعی که تابع در یک خط لوله به کار میرود(یا هر وضعیت دیگر که در آن خروجی استاندارد ترمینال نیست) بعضی پرچمهای خاص را غیر فعال میکند.
برای بحث بیشتر در مورد استفاده از توابع به جای مستعارها پرسش و پاسخ شماره 80 را ببینید.
پرسش و پاسخ 86 (آخرین ویرایش 2009-08-30 21:09:35 توسط localhost)
در یک حالت کلی، یک توصیفگرفایل (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 آن را خاتمه بدهید).
پرسش و پاسخ 85 (آخرین ویرایش 2013-03-07 17:00:39 توسط put92-6-88-165-39-120)
توابع در Bash (همچنین سایر پوستههای هم خانواده شل Bourne) مانند فرمانها کار میکنند: یعنی، آنها فقط یک وضعیت خروج برگشت میدهند که منحصر به اعداد صحیح از 0 تا 255 است. این به منظور استفاده در علامتدهی خطاها میباشد، نه برای بازگرداندن نتایج محاسبات یا سایر دادهها.
اگر نیاز دارید دادههای اختیاری را از یک تابع به فراخواننده آن باز گردانید، حداقل سه شیوه وجود دارد که توسط آنها میتوانید این نیاز را برآورده کنید:
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 را ملاحظه کنید.
پرسش و پاسخ 84 (آخرین ویرایش 2012-10-16 17:56:51 توسط GreyCat)
چندین روش بر حسب موارد نیاز، برای بررسی یک متغیر تعریف شده یا غیرتهی یا تابع وجود دارد.
محتویات
همانند بسیاری از زبانها تعیین و تعریف میتوانند مراحل مستقلی باشند. تعیین(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)