در بسیاری از زبانها، نقلقولها اساساً برای مشخص نمودن آنکه متن محصور بین آنها به عنوان یک نوع دادهِ رشتهای تفسیر گردد، به کار میروند، اما در برنامهنویسی پوسته، تقریباً همه چیز یک رشته است، بنابراین نقلقول در پوسته اثرات و مقاصد بسیار متفاوتی دارد. چند نوع نقلقول وجود دارد که اصولاً روشهای متفاوتی از تفسیر محتویاتشان را فعال میکنند. متأسفانه، غالباً یادگیری قواعد آن برای نوآموزان دشوار است و در اِزای چند حالت ویژه و استثناهایی برای به خاطر سپردن، رفتارشان با متن تغییر میکند. همچنین متأسفانه، نقلقول در برنامهنویسی پوسته بینهایت اهمیت دارد. از مواردی است که شخص نمیتواند از یادگیری آن اجتناب کند. نقلقول نادرست در پوسته یکی از رایجترین منابع باگهای اسکریپتنویسی و مسائل امنیتی میباشد. خوشبختانه، اکثر اوقات انجام درست آن با پیروی از چند راهبرد امکان پذیر است، اما در مورد نقلقول حدسی عمل نکنید. وقتی مردد هستید، تست کنید، و صفحه man در مورد چگونه تفسیر شدن نقلقولها در یک زمینه مفروض را بخوانید.
مندرجات
نقلقولهای منفرد: (آپاستروفها) از هرگونه تفسیر ویژه کاراکترهای میان آنها ممانعت میکنند. هر چیزی درمیان نقلقولهای منفرد لفظی است و به یک کلمه منفرد بسط مییابد. تنها موردی که نمیتواندنقلقولی منفرد بشود، خود کاراکتر نقلقول منفرد است.
نقلقولهای دوگانه: مهمترین و پیچیدهترین نوعِ نقلقول در برنامهنویسی پوسته میباشند. نقلقولهای دوگانه اجازه میدهند پوسته محتوای آنها را برای انواعی از بسطها که با یک علامت $ پیشوند میشوند - بسط پارامترها، بسطهای حسابی و جایگزینیهای فرمان - تجزیه کند، در حالیکه با هر محتوای دیگر (غیر از backtickهای موروثی (`) تشریح شده در پایین و بسط تاریخچه (!)) به طور لفظی رفتار میکنند. مهمتر از همه، در داخل نقلقولهای دوگانه تفکیک کلمه و بسط نام فایل بر چیزی اِعمال نمیگردند، همچنین آنها محتوایشان را از بسط ابرو، بسط مَد، جایگزینی پردازش، وتفسیر شدن به عنوان ترکیب دستوری تغییر مسیر نیز محافظت میکنند. نقلقولهای دوگانه نیز مانند نقلقولهای منفرد گنجاندن فوقکاراکترها و عملگرهای کنترل را بدون پوشش دادن توسط کاراکتر
$'...' و $"...": پوسته Bash دو شکل اضافی نقلقول را ارائه میکند -- نقلقولهای منفرد و دوگانهِ مقدم شده به وسیله یک علامت $ . البته، $'...' رایجتر است و درست مانند نقلقول منفرد عمل میکند به استثنای آنکه ترکیبات پوشش یافته توسط
گریز: کاراکتر گریز در Bash و POSIX sh کاراکتر
توجه: اگر چه backtickها (`) از نظر زبانشناسی نوعی نقلقول هستند، آنها در bash عملاً چیزی را نقلقول نمیکنند. در جایی که نقلقول در bash برای لفظی کردن(جزیی) دادهها به کار میرود، backtickها عملی کاملاً متفاوت انجام میدهند. آنها ترکیب دستوری قدیمی بورن برای جایگزینی فرمان هستند. آنها اساساً همان کار $(...) را انجام میدهند، اما یک مشت استثناهای شگرف نیز وجود دارد. به هر حال، آنقدر مهم نیستند: به خاطر وجود $(...) استفاده از آنها تقبیح میشود، و شما به راستی این روزها نباید این چیزها را در کد مدرن یا ابقا شده bash به کار ببرید. با وجود این اگر آنها را ببینید، نیاز به نقلقول را باطل نمیکنند(درست مانند "$(command)"، باید آنها را نقلقول دوگانه کنید: "`command`")، و احتمالاً فقط باید آنها را با $() تعویض نمایید.
یک فرمان پوسته با استفاده از فضاهای سفید (صرف نظر از متغیر IFS ) و سایر فوقکاراکترهای پوسته، بوسیله شل به کلمات تجزیه میگردد. اولین عملکرد نقلقول، اجازه دادن به کلمات برای در بر داشتن این فوقکاراکترها میباشد.
echo '&'
بدون نقلقولها، فوقکاراکتر & فرمان echo را در پسزمینه قرار خواهد داد. با نقلقولها، فوقکاراکتر & واقعاً یک کلمه راتشکیل میدهد، و به عنوان شناسه به فرمان echo عبور داده میشود.
نقلقولها عملاً به فرمان عبور داده نمیشوند.آنها توسط پوسته حذف میگردند (این فرایند هوشمندانه «پاکسازی نقلقول» نامیده میشود). در مثال فوق، فرمان echo فقط & را میبیند، نه نقلقولها را.
دومین هدف نقلقول ممانعت از تفکیک کلمه و globbing است. نتیجه یک جایگزینی نقلقول شده منفرد یا دوگانه، تحت تأثیر پردازش بیشتری قرار نمیگیرد. (نتیجه یک جایگزینی نقلقولی نشده قرار میگیرد.)
cp -- "$filename" "$destination"
در این مثال، نقلقولهای دوگانه، کمیت هر پارامتر (متغیر) را از تحت تأثیر تفکیک کلمه قرار گرفتن یا انجام globbing روی فضای سفید و کاراکترهای عامِ (* یا ? یا [...]) محتوان آنها محافظت میکنند. بدون نقلقولها، یک filename مانند hot stuff.mp3 به دو کلمه تجزیه میگردید، و هر کلمه به عنوان یک شناسه جداگانه به فرمان cp عبور داده میشد. یا، یک filename شامل * همراه با فاصله در اطراف آن، به ازای هر فایل داخل دایرکتوری جاری یک کلمه تولید میکرد. که آنچه ما میخواهیم، نیست.
در اثر نقلقولها، با هر کاراکترِ داخل کمیتِ پارامتر filename به طور لفظی رفتار میگردد، و کل کمیت، شناسه دوم فرمان cp میشود.
وقتی تردید دارید، همیشه بسط پارامترهایتان را نقلقول دوگانه کنید.
اگر احتیاج داشته باشید، میتوانید انواع مختلف نقلقولها را با هم به کار ببرید. برای مثال، اگر رشتهای داشته باشید که یک قسمت از آن دارای کاراکترهای ویژه زیادی میباشد که میخواهید نقلقول منفرد کنید و یک بسط پارامتر درون یک قسمت دیگر رشته است که باید نقلقول دوگانه شود، میتوانید آنها رامخلوط کنید:
echo '!%$*&'"$foo"
هر تعداد از زیر رشتههای نقلقولی شده از هر نوع، در این حالت میتوانند به یکدیگر الحاق گردند. نتیجه (پس از بسطهای مناسب در بخشهای نقلقول دوگانه شده) یک کلمه منفرد است.
نقلقول دوگانهِ $@ یا ${array[@]} دارای معنای ویژهای میباشد. "$@" به لیستی از کلمات، که مقدار هر پارامتر مکانی یک کلمهِ لیست میشود، بسط مییابد. به همچنین "${array[@]}" به لیستی از کلمات به ازای هر عضو آرایه بسط مییابد. موقع کار کردن با پارامترهای مکانی یا محتوای یک آرایه به عنوان لیستی از کلمات، همیشه ترکیب نقلقول دوگانه را به کار ببرید.
نقلقول دوگانهِ $* یا ${array[*]} به یک کلمه نتیجه میشود که الحاق تمام پارامترهای مکانی (یا عناصر آرایه) با اولین کاراکتر متغیر IFS در میان آنها میباشد. این مورد مشابه تابع join در سایر زبانهاست، اگر چه واقعیتِ آنکه فقط یک کاراکتر اتصال دارید گاهی اوقات محدودیت فلجکنندهای است.
این هم چند مثال گوناگون برای نشان دادن آنکه کارها چگونه باید انجام گردد. برخی از این مثالها ترکیب دستوری bash/ksh را به کار میبرند که در پوسته های POSIX محض کار نخواهند کرد.
تکرار صحیح روی پارامترهای مکانی با کاربرد "$@" نقلقولی شده. هرگز از یک $@ یا $* نقلقولی نشده استفاده نکنید.
for file in "$@"; do ... done
همچون مورد فوق، اما با یک آرایه.
for element in "${array[@]}"; do ...
تکرار صحیح روی شاخصها
# و بالاتر 3.0 نگارش bash for index in "${!array[@]}"; do ...
تمام بسطهای متداول، بر متن داخل پرانتزهای یک تخصیص مرکب آرایه اِعمال میگردند، از جمله تفکیک کلمه و بسط نام مسیر، و باید به همان روش که اگر به عنوان شناسهها به یک فرمان عبور داده میشدند، نقلقولی و با کاراکتر گریز پوشانده شوند.
# bash or ksh93 find_opts=('(' -iname '*.jpg' -o -iname '*.gif' -o -iname '*.png' ')') find . "${find_opts[@]}" -print
اولی، یک رشته نقلقول منفرد شده، که با یک علامت نقلقول منفردِ نقلقول نشدهِ پوششیافته با کاراکتر گریز دنبال میشود و پس از آن یک رشته نقلقولی منفرد. دومی، یک معادل غیر POSIX آن با کاربرد $'...'
echo 'Don'\''t walk!' echo $'Don\'t talk!'
جایگزینیهای فرمان به سبک $(...) از این حیث که نقلقول محتوایشان کاملاً مستقل از احاطه کنندههای آنها هستند، منحصر به فرد میباشند. این به معنای آنست که شما نباید نگران مسائل معاف کردن(escaping) نقلقول تو در تو باشید.
echo "The matching line is: $(grep foo "$filename")" # .تودرتو هستند $() توجه کنید که نقلقولهای داخل جایگزینی فرمان # .اشتباه است، اما در شلها صحیح میباشد C این مورد از نظر یک برنامهنویس echo "The matching line is: $(grep foo "$filename")" # ^---------^ لایه داخلی نقلقولها # ^^--------------------^ لایه میانی - جایگزینی فرمان # ^---------------------------------------------^ لایه خارجی نقلقولها # گردد، grep قبل از اینکه تحویل $filename بدون نقلقولهای درونی مقدار # میشد (globed) تفکیک کلمه و جانشین
# bash ip=192.168.1.30 netmask=255.255.254.0 IFS=. read -ra ip_octets <<<"$ip" IFS=. read -ra netmask_octets <<<"$netmask" for i in 0 1 2 3; do ((ip_octets[i] &= netmask_octets[i])) done IFS=.; network="${ip_octets[*]}"; unset IFS
نقلقول سبکِ $'...' برای تفسیر backslash escapeها به کار میرود.
IFS=$' \t\n' # ،را به یک رشته سهبایتی شامل یک فاصله IFS متغیر # .و یک سطر جدید تنظیم میکند tab یک
# :موارد زیر همگی همارز هستند echo $'hi\nthere\n' printf '%s\n' hi there # :سست شده echo -e 'hi\nthere\n' # ksh93 در پوسته print 'hi\nthere\n'
یک وضعیت مهم، جایی که نقلقولها میتوانند یک تأثیر ناخواسته داشته باشند، در متنهای انطباق الگو میباشد. Bash نقلقولها را برای موقوف کردن «ویژگی» الگوها و عبارتهای منظم به کار میبرد. با کاراکترهایی که در یک متن انطباق الگو به طور خاص تفسیر میشوند، موقعی که نقلقولی باشند خواه الگو از یک پارامتر بسط داده شود یا خیر، به طور لفظی رفتار میشود.
if [[ $path = foo* ]]; then # عمل میکند glob نقلقولی نشده به عنوان یک foo* if [[ $path = "foo*" ]]; then # نقلقولی شده یک رشته لفظی است "foo*" if [[ $path =~ $some_re ]]; then # (عبارت منظم توسعهیافته) عمل میکند ERE به عنوان یک $some_re محتویات if [[ $path =~ "$some_re" ]]; then # به عنوان یک رشته لفظی رفتار میشود $some_re با محتویات =~ با وجود عملگر
# Bash 4 # the "quoted" branch is taken. g=a*[b] case $g in "$g") echo 'quoted pattern' ;;& $g) echo 'unquoted pattern' ;; esac
پاکسازی نقلقول هرگز بر محتوای یک here document اِعمال نمیشود.
$ arr=(words in array); cat <<EOF > These are the "${arr[@]}" > EOF These are the "words in array"
نقلقول «رشته نگهبان» در heredoc باعث خواهد شد متنهای heredoc برای بسطها تجزیه شوند(مترجم: منظور این است که مثلاً $HOME همین طور دست نخورده باقی میماند که بعداً بسط داده شود). این نوع heredoc نقلقول شده قدری خاص میباشد، به علت اینکه در تمام bash تنها موردی است که در آن یک رشته لفظی از یک متن انتخابی مشخص میشود که خودش موضوع هیچگونه تفسیر ویژه نیست، بلکه برای علامت زدن شروع سطرهای heredoc است.
{ echo before eval "$(</dev/stdin)" # .مثل اینکه اینجا بوده اجرا میشود heredoc echo after } <<\EOF # در اینجا قرار ندهید bash ابداً هیچ کُد EOF
نقلقولها (آخرین ویرایش 2013-01-05 09:40:48 توسط ormaaj)
مترجم: به دلیل اهمیت مطلب و برای کاملتر نمودن آن، دومین پیوند از بخش See also صفحه فوق یعنی http://wiki.bash-hackers.org/syntax/quoting را نیز ترجمه و در ادامه این صفحه ضمیمه نمودهام.
escape و escaping برای لغو نمودن معنای ویژه کاراکترهای خاص و لفظی کردن آنها با استفاده از کاراکتر گریز (\) به کار میرود، من در این ترجمه به جای آن «گریز» یا «معاف کردن» و «معاف» را به کار بردهام.
در حقیقت نقلقول و گریز روش مهمی برای تحت تأثیر قرار دادن رفتار Bash با ورودی شماست. سه نوعِ به رسمیت شناخته شده از آن وجود دارد:
هر سه شکل دقیقاً دارای مفهوم یکسان هستند: آنها کنترل متداول روی تجزیه، بسط و نتایج بسط را به شما ارائه میکنند.
در کنار این گونههای اصلی، برخی روشهای نقلقول خاصتری (مانند تفسیر ANSI-C escapeها در یک رشته) را در پایین ملاحظه خواهید نمود.
توجه این کاراکترهای نقلقول ("، نقلقول دوگانه و '، نقلقول منفرد) یک جزء گرامری هستند که تجزیه را تحت تأثیر قرار میدهند. به کاراکترهای نقلقول احتمالی که به عنوان متن به سطر فرمان عبور داده میشوند، مربوط نمیگردند! ترکیب نقلقولها قبل از آنکه فرمان احضار شود حذف میشوند! نگاه کنید:
MYARG="\"my multiword argument\"" command $MYARG ### :نه نه نه: این سه رشته عبور میدهد ### (1) "my ### (2) multiword ### (3) argument" ### کُد فوق مانند این نیست ### command "my multiword argument" ### به این نیاز دارید### MYARG="my multiword argument" command "$MYARG"
معاف کردن یک کاراکتر در محلهای مختلفی به کار میآید، همچنین در بسطها و جایگزینیها . به طور کلی،کاراکتری که برای Bash دارای معنی ویژه میباشد، مانند علامت دلار ($) جهت معرفی انواع بسط، میتواند برای نداشتن آن معنی ویژه با کاربرد کاراکتر
echo \$HOME is set to \"$HOME\"
رشته \<newline> (یک backslash غیر نقلقولی، دنبال شده با یک کاراکتر سطرجدید) به عنوان ادامه سطر تفسیر میگردد. از جریان ورودی حذف میشود و بنابراین به طور مؤثر صرفنظر خواهد شد. برای آرایش دادن به کُدتان آن را به کار ببرید:
# escapestr_sed() # یک جریان را از ورودی استاندارد میخواند و کاراکترهای داخل متن را که میتوانستند # کاراکترهای خاص تفسیر گردند، معاف میکند sed توسط escape_sed() { sed \ -e 's/\//\\\//g' \ -e 's/\&/\\\&/g' }
کاراکتر backslash میتواند برای نقاب زدن به هر کاراکتری که دارای معنای خاص برای bash میباشد، به کار برود. استثناء: درون یک رشته نقلقولی منفرد (پایین را ببینید).
داخل یک رشته نقلقولی ضعیف، تفسیر ویژهای از این موارد وجود ندارد:
هر مورد دیگر، بخصوص بسط پارامتر، انجام میشود!
ls -l "*"بسط داده نخواهد شد. ls کاراکتر لفظی * را به عنوان شناسه دریافت میکند. یک خطا بیرون خواهد داد مگر اینکه یک فایل با نام * داشته باشید.
echo "Your PATH is: $PATH"همانطور که انتظار میرود عمل خواهد نمود. $PATH بسط داده میشود، زیرا تنها نقلقولی دوگانه(ضعیف) شده است.
اگر یک backslash در نقلقول دوگانه (نقلقول ضعیف) واقع شود، دو روش برای رفتار با آن وجود دارد
بویژه این بدان معناست که "\$" تبدیل به $ خواهد شد، اما "\x" تبدیل به \x میشود.
نقلقول قوی برای توضیح دادن بسیار ساده است
داخل یک رشته نقلقولی منفرد هیچ چیزی (!!!!) تفسیر نمیشود، به استثنای نقلقول منفرد که نقلقول را میبندد.
echo 'Your PATH is: $PATH'آن $PATH بسط نخواهد یافت، به عنوان متن عادی معمولی تفسیر میگردد، زیرا به وسیله نقلقولهای قوی احاطه گردیده است.
در عمل به معنای آن است که، برای ارائه یک متن مانند Here's my test… به عنوان یک رشته نقلقولی منفرد، شما برای بدست آوردن کاراکتر "'" به عنوان متن لفظی، باید از نقلقول منفرد خارج و دوباره وارد شوید:
# اشتباه echo 'Here's my test...' # صحیح echo 'Here'\''s my test...' # :پیشنهاد: ترکیب کردن نقلقولها برای خوانایی نیز امکانپذیر است echo "Here's my test"
یک مکانیسم دیگر نقلقول هم وجود دارد که Bash عرضه میکند: رشتههایی که بواسطه رشتههای گریز ANSI C مد نظر قرار میگیرند. ترکیب دستوری آن به این شکل است:
$'string'که در آن رشتههای گریز پایین در string کُد برداری میشوند:
کُد | معنی |
---|---|
\" | نقلقول دوگانه |
\' | نقلقول منفرد |
\\ | backslash |
\a | کاراکتر اعلام ترمینال(زنگ) |
\b | backspace |
\e | escape (ASCII 033) |
\E | escape (ASCII 033) کد \E غیر استاندارد است |
\f | تغذیه کاغذ چاپگر |
\n | سطر جدید |
\r | سر سطر رفتن |
\t | tab افقی |
\v | tab عمودی |
\cx | یک کاراکتر control-x، برای مثال $'\cZ' برای چاپ رشته کنترلی ترکیب شده کنترل و Z یعنی (^Z) |
\uXXXX | XXXX را به عنوان یک عدد هگزا دسیمال تفسیر کرده و کاراکتر معادل آن از مجموعه کاراکتر(۴ رقم) را چاپ میکند (Bash 4.2-alpha) |
\UXXXXXXXX | XXXX را به عنوان یک عدد هگزادسیمال تفسیر نموده وکاراکتر معادل از مجموعه کاراکتر(۸ رقم) چاپ میکند (Bash 4.2-alpha) |
\nnn | کاراکتر ۸ بیتی که مقدار آن عدد اکتال nnn میباشد (یکی برای سه رقم) |
\xHH | کاراکتر ۸ بیتی که مقدار آن عدد هگزادسیمال HH میباشد (یکی برای دو رقم هگز) |
این مورد مخصوصاً موقعی مفید است که میخواهید کاراکترهای خاص را به عنوان شناسه به برخی برنامهها ارائه کنید، مانند ارائه یک سطرجدید به sed.
با متنِ نتیجه چنان رفتار میشود که اگر نقلقولی منفرد شده بود. بسطها دیگر صورت نمیگیرند.
ترکیب دستوری $'...' در ksh93 ایجاد شده، اما به اکثر پوستههای مدرن از جمله pdksh قابل حمل میباشد. یک مشخصات برای آن بواسطهSUS 7 صادره، پذیرفته شد. باز هم جدا افتادههایی از قبیل اکثر گونههای ash شامل dash وجود دارند (غیر از busybox ساخته شده باویژگیهای «سازگاری باbash»).
یک علامت دلار دنبال شده با رشته نقلقولی دوگانه، برای مثال
echo $"generating database..."به معنی محلیسازی است. اگر ترجمه معتبر برای آن رشته وجود داشته باشد، به جای متن داده شده به کار میرود. اگر نباشد، یا منطقه C/POSIX باشد، به سادگی علامت دلار صرفنظر میگردد که به یک رشته نقلقولی دوگانه عادی منجر میشود.
اگر رشته تعویض (ترجمه) شده باشد، نتیجه نقلقولی دوگانه است.
در صورتی که شما برنامهنویس C هستید: مقصود از $"…" مانند gettext() یا _() است.
برای مثالهای مناسب جهت محلیسازی اسکریپتهایتان، لطفاً پیوست I از راهنمای اسکریپتنویسی پیشرفته را ببینید.
توجه: یک حفره امنیتی وجود دارد. لطفاً مستندات gettext را بخوانید
حلقه کلاسیک for یک لیست از کلمات را برای تکرار روی آنها به کار میبرد. البته این لیست در یک متغیر نیز میتواند باشد:
mylist="DOG CAT BIRD HORSE"
روش نادرست برای تکرار روی این لیست:
for animal in "$mylist"; do echo $animal done
چرا؟ مطابق اصول تکنیکی، به علت نقلقول دوگانه، بسط $mylist به صورت یک کلمه دیده میشود. حلقه for دقیقاً یکبار با تنظیم animal معادلِ تمام لیست، تکرار میگردد.
روش درست برای تکرار در سرتاسر این لیست:
for animal in $mylist; do echo $animal done
فرمان test یا [ … ] ( فرمان کلاسیک test) یک فرمان عادی معمولی است، بنابراین قواعد گرامر فرمان عادی معمولی اِعمال میگردد. اجازه بدهید مقایسه رشتهای را به عنوان مثال بیاوریم:
[ WORD = WORD ]
شناسه ] در انتها یک قرارداد است، اگر شما فرمان which [ را تایپ کنید خواهید دید که در واقع یک فایل باینری با آن نام ([) وجود دارد. بنابراین اگر این را به عنوان یک فرمان test مینوشتیم، اینطور میشد:
test WORD = WORD
موقعی که متغیرها را مقایسه میکنید، نقلقول کردن آنها خردمندانه است. اجازه بدهید یک رشتهِ تست با فاصلهها بسازیم:
mystring="my string"
و اکنون آن رشته را در برابر "testword" کنترل کنیم:
[ $mystring = testword ] # !!!اشتباه
این مقایسه ناموفق است! شناسههای بیش از اندازهای برای مقایسه test وجود دارد. پس از انجام تمام بسطها، در حقیقت این مورد را اجرا میکنید:
[ my string = testword ] test my string = testword
که اشتباه است، زیرا my و string دو شناسه جداگانه میباشند.
بنابراین آنچه واقعاً نیاز دارید انجام بدهید، این است:
[ "$mystring" = testword ] # !!!درست
test 'my string' = testword
اکنون فرمان دارای سه پارامتر است، که بواسطه یک عملگر باینری (دو شناسه) معقول است.
نکته: داخل عبارت شرطی ([[ ]]) تفکیک کلمه توسط Bash انجام نمیشود، و از اینرو شما به نقلقول نمودن متغیرهای مرجع خود نیاز ندارید - آنها همواره به صورت «یک کلمه» دیده میشوند.
syntax/quoting.txt · آخرین ویرایش: 2013/07/07 12:34 توسط ormaaj