در اینجا پیوندهایی به مستندات رسمی Bash آمده است:
NEWS: فایل لیست موجز تغییرات بین نگارش جاری و نگارش قبلی
CHANGES: تاریخچه کامل تغییرات bash (فقط از نگارش2.0 به بعد)
COMPAT: مسائل سازگاری بین bash3 و نگارشهای قبلی
لیست خلاصه گستردهتر از لیست پایین میتواند دراینجا یافت شود تغییرات Bash
این هم لیست جزئی تغییرات، در یک قالب فشردهتر:
اضافه شده در نگارش |
|
\uXXXX and \UXXXXXXXX |
4.2-alpha |
declare -g |
4.2-alpha |
test -v |
4.2-alpha |
printf %(fmt)T |
4.2-alpha |
array[-idx] and ${var:start:-len} |
4.2-alpha |
lastpipe (shopt) |
4.2-alpha |
read -N |
4.1-alpha |
{var}> or {var}< etc. (FD variable assignment) |
4.1-alpha |
syslog history (compile option) |
4.1-alpha |
BASH_XTRACEFD |
4.1-alpha |
;& and ;;& fall-throughs for case |
4.0-alpha |
associative arrays |
4.0-alpha |
&>> and |& |
4.0-alpha |
command_not_found_handle |
4.0-alpha |
coproc |
4.0-alpha |
globstar |
4.0-alpha |
mapfile/readarray |
4.0-alpha |
${var,[,]} and ${var^[^]} |
4.0-alpha |
{009..012} (leading zeros in brace expansions) |
4.0-alpha |
{x..y..incr} |
4.0-alpha |
read -t 0 |
4.0-alpha |
read -i |
4.0-alpha |
x+=string array+=(string) |
3.1-alpha1 |
printf -v var |
3.1-alpha1 |
{x..y} |
3.0-alpha |
${!array[@]} |
3.0-alpha |
[[ =~ |
3.0-alpha |
<<< |
2.05b-alpha1 |
i++ |
2.04-devel |
for ((;;)) |
2.04-devel |
/dev/fd/N, /dev/tcp/host/port, etc. |
2.04-devel |
a=(*.txt) file expansion |
2.03-alpha |
extglob |
2.02-alpha1 |
[[ |
2.02-alpha1 |
builtin printf |
2.02-alpha1 |
$(< filename) |
2.02-alpha1 |
** (exponentiation) |
2.02-alpha1 |
\xNNN |
2.02-alpha1 |
(( )) |
2.0-beta2 |
پرسش و پاسخ شماره 61 (آخرین ویرایش 2012-05-07 12:12:43 توسط geirha)
این را ملاحظه نمایید:
#!/bin/sh cd /tmp
اگر شخصی این اسکریپت را اجرا نماید، چه اتفاقی روی میدهد؟ Bash منشعب میشود که به یک والد(پوسته محاورهای که فرمان در آن تایپ شده) و یک فرزند(پوسته جدیدی که اسکریپت را میخواند و اجرا میکند) منجر میگردد. فرزند اجرا میشود، در حالیکه پدر برای خاتمه یافتن آن منتظر میماند. فرزند اسکریپت را میخواند و اجرا میکند، دایرکتوری جاری آن به /tmp تغییر میکند، و سپس خارج میشود. والد که در انتظار فرزند است،کد وضعیت فرزند را تحصیل میکند(احتمالاً صفر در اثر موفقیت)، و سپس با دستور بعدی ادامه میدهد. هیچ جایی در این پردازش دایرکتوری کاری پدر تغییر نکرده است -- فقط دایرکتوری فرزند تغییر کرد.
پردازش فرزند هرگز بر هیج بخشی از محیط والد، که شامل متغیرهایش، دایرکتوری کاریاش، فایلهای بازش، محدودیت منابعش، و غیره میشود، اثر نمیگذارد.
بنابراین، چطور فردی تقلا میکند دایرکتوری جاریوالد را تغییر بدهد؟ هنوز میتوانید فرمان cd را در یک فایل خارجی داشته باشید، اما نمیتوانید به عنوان اسکریپت آنرا اجراکنید. اجرای آن باعث انشعابی که قبلاً تشریح شد، میگردد. به جای آن شما باید با دستور .(یا دستور source مترادف آن، فقط در Bash) آن را منبع کنید. منبع کردن اساساً به معنی آنست که شما فرمانها را در یک فایل با استفاده از پوسته جاری اجرا میکنید، نه در یک پوسته منشعب(شل فرزند):
echo 'cd /tmp' > "$HOME/mycd" # ایجاد میکند 'cd /tmp' فایلی محتوی فرمان . $HOME/mycd # در شل جاری 'cd /tmp' آن فایل منبع میشود، اجرای فرمان pwd # هستیم /tmpاکنون ما در شاخه
همین مورد در تنظیم متغیرها اِعمال میگردد. . ("dot in") فایلی که شامل دستورات است، سعی نمیکند آن فایل را اجرا کند.
اگر دستوری که اجرا میکنید یک تابع است، نه یک اسکریپت، در پوسته جاری اجرا خواهد شد. بنابراین، ممکن است برای انجام آنچه در مثال فوق سعی کردیم با فایل خارجی انجام بدهیم، بدون احتیاج به هر گونه "dot in" یا "source" یک تابع تعریف شود. تعریف تابع زیر و سپس فراخوانی ساده آن با تایپ mycd:
mycd() { cd /tmp; }
اگر میخواهید به طور خودکار در پوسته جدیدی که باز میکنید تابع در دسترس باشد، آن را در ~/.bashrc یا مشابه آن قرار بدهید.
بعضی اشخاص ترجیح میدهند از مستعارها در عوض توابع استفاده کنند. توابع بیشتر قدرتمند ، عمومیتر، قابل انعطافتر، و.... میباشند، فقط به نظر میرسد بعضی افراد آنها را دوست ندارند.
alias mycd='cd /tmp' # .معادل تابع نشان داده شده در بالا
alias cdlstmp='cd /tmp && ls tmp*' # شروع میشوند "tmp" میبرد و نشان میدهد آنجا کدام فایلها با /tmp شما را به
cdls() { cd "$1" && ls; }
# توسط مستعارها نمیتواند انجام شود
# cdls directory نحوه استفاده
پرسش و پاسخ 60 (آخرین ویرایش 2012-01-27 22:50:33 توسط 125)
این به طور بالقوه یک فرمان خطرناک است. آن را اجرا نکنید! راهاندازی از پرسش فوق حذف میشود، تنها آن قسمتی که تابع را تنظیم میکند باقی میماند.
بمب خوشهای یک شکل ساده تکذیب سرویس(یا حمله DoS) است که بر مبنای نام فراخوان سیستم یونیکسیِ fork(2) نام گذاری گردیده است. برنامهای است که توسط انشعاب کپیهای خودش به طور مکرر، که فرزندان نیز به طور بازگشتی همان کار را میکنند، به سرعت منابع سیستم را تحلیل میبرد. در بسیاری از سیستمهایِ بدون محدودیت صحیحِ منابع، این مورد ممکن است شما را در یک وضعیت اصلاح ناپذیر غیرپاسخگو رها کند.
این تعریف ویژه از بمب fork در Bash بنا به دلایلی چنان مشهور است که گاهی اوقات بمب خوشهای نامیده میشود.
در اینجا رایجترین شکل مورد پسند عامه کد آن آمده است:
:(){ :|:& };:
و از طرف دیگر، با قواعد مناسب برای خوانایی به این صورت:
#!/usr/bin/env bash
:() {
: | : &
}
:
این کد تابعی به نام : تعریف میکند. بدنه تابع یک لوله را تنظیم میکند، که در Bash متشکل از دو پوسته فرعی میباشد، خروجی استاندارد اولی توسط یک لوله به ورودی استاندارد دومی متصل گردیده است. تابع (پوسته والد لوله) لوله را پسزمینهای میسازد، تابع باز میگردد و پوسته با رها نمودن job در پس زمینه، خاتمه مییابد. نتیجه نهایی دو پردازش جدید است که هر کدام پردازش : را برای تکرار فرآیند، فراخوانی میکنند.
: در واقع در اکثر شرایط ( پایین تشریح شده) یک نام غیر مجاز تابع میباشد. اینجا، bomb به جای : استفاده میشود، که هم قابل حمل است و هم خوانایی بهتری دارد.
bomb() {
bomb | bomb &
}
bomb
به طور نظری، هر شخصی که در کامپیوتر شما به پوسته دسترسی دارد، میتواند از چنین تکنیکی برای نابود کردن منابعی که به آن دسترسی دارد استفاده کند. در اینجا یک chroot(2) کمک نخواهد بود. اگر منابع کاربر نامحدود باشد، آنوقت در چند ثانیهای تمام منابع سیستم شما(پردازشها، حافظه مجازی، فایلهای باز، وغیره.) استفاده خواهد شد و احتمالاً خودش دچار وقفه میشود. هر کوشش به عمل آمده توسط کرنل برای آزادسازی منابع بیشتر فقط اجازه میدهد نمونههای بیشتری از تابع ایجاد بشود.
در نتیجه، تنها راه محافظت از خودتان در برابر چنین سوءاستفادهای، محدودیتِ حداکثرِ مجازِ استفاده از منابع برای کاربرانتان میباشد. چنین منابعی به وسیله فراخوان سیستمی setrlimit(2) مقرر میشود. واسط این قابلیت در Bash و پوسته Korn فرمان ulimit است. همچنین ممکن است سیستم عامل شما فایلهای پیکربندی ویژهای برای کمک به مدیریت این منابع داشته باشد( برای مثال، فایل /etc/security/limits.conf در دبیان، یا /etc/login.conf در OpenBSD). برای جزئیات، مستندات سیستم خود را کنکاش نمایید.
این تعریف عامه پسند به علت یک ترکیب غیرعادی جزئیات (در پوستههایی که من با آنها تست کردهام) که فقط در Bash غیر POSIX و Zsh(تمام حالتهای شبیهسازی) پیش میآید، کار میکند.
پوسته باید تعریف نامهای تابع، ماورای آنها که به واسطه یک POSIX "Name" مجاز هستند را اجازه بدهد. این مطلب بلافاصله ksh93 و Bash در وضعیت POSIX و Dash و Posh ( Posh یک شاخه قدیمی pdksh است که دیگر پشتیبانی نمیشود)، و Busybox sh را رد میکند.
به طور نادرست توابعی مقرر کند که داخلیهای ویژه را پیش از داخلی خودش بارگذاری میکند. جستجو و اجرای فرمان را ببینید. mksh در این مرحله(به طور صحیح) ناموفق میشود، واقعاً دستور : داخلی را اجرا میکند. حتی اگر شما تابع را به طور موفقیت آمیز تعریف کنید، فراخوانی تابع غیر ممکن است. Bash در وضعیت غیرPOSIX و Zsh (حتی شبیهساز POSIX)مطابق این ضوابط هستند.
$ bash -c 'enable -d :; type -p :'
bash: line 0: enable: :: not dynamically loaded
$ ksh -c 'builtin -d :; whence -v :'
ksh: whence: :: not found
احتمالاً یک باگ: -d **.هر داخلی تعریف شده را حذف میکند. **داخلی ویژه نمیتواند حذف بشوددر هر صورت، نامربوط است زیرا ksh93 قبلاً در مرحله اول ناموفق شده است. اکنون شما فقط یک داخلی غیرقابل دسترس دارید.
بنابراین به طور خلاصه، این forkbomb خیلی جالب نیست. اساساً تعریف متعارفی است که به طور مبتذل توسط تخصیص یک نام ناشناس که تقریباً در هر جایی ناموفق است، گیج کننده گردیده است. بنا به ادعای فرضی کنایهدار نویسنده اصلی، شخص میتواند در هر ترمینال یونیکس تایپ کند:
:(){ :|:& };:
پرسش و پاسخ 59 (آخرین ویرایش 2013-01-08 18:58:36 توسط GreyCat)
به طور اساسی پاسخ خیر است....
در حالیکه bash مشکلاتی به زیادی پوستههای قدیمیتر با آنها ندارد، باز هم نمیتواند دادههای باینری اختیاری را پردازش نماید، و به طور اخص، متغیرهای پوسته 100% باینری خالص نیستند، بنابراین نمیتوانید فایلهای باینری را در آنها ذخیره کنید.
شما میتوانید دادههای اسکی کُدگذاری شده یونیکس به یونیکس(uuencoded) را به این طریق در متغییر قرار دهید:
var=$(uuencode /bin/ls ls)
cd /somewhere/else
uudecode <<<"$var" # نقلقولها را فراموش نکنید
توجه: تفاوت سترگی میان uuencode یا uudecode گنو و یونیکس وجود دارد. با uudecode یونیکس، شما نمیتوانید فایل خروجی تعیین کنید، همواره از نام فایل کدگذاری شده در داده اسکی استفاده میکند. من مثال قبلی را اصلاح کردهام به طوری که در سیستمهای یونیکس کار میکند. اگر شما تغییرات بیشتری ایجاد میکنید، لطفاً رویهگرایی گنو (GNUisms) را به کار نبرید. متشکرم. --GreyCat
یک نمونه که چنین موردی ممکن است گاهی در آن سودمند باشد، ذخیره کردن نقشهبیتیهای کوچک موقتی، در هنگام کار کردن با netpbm است... در اینجا من به یک pnmnoraw زیرنویس 1 اضافی در لوله متوسل شدم که باعث ایجاد فایلهای اسکی بزرگتر میشود و bash با ذخیره آنها مشکلی ندارد.
اگر احساس ماجراجویی دارید، این تجربه را ملاحظه نمایید:
# bindec.bash, سعی میکند دادههای باینری را به اسکی دسیمال رمزگشایی کند
IFS=
while read -n1 x ;do
case "$x" in
'') echo empty ;;
# ۲۵۶ سطر تولید شده توسط سطر فرمان زیر را در اینجا درج کنید
# for x in $(seq 0 255) ;do echo " $'\\$(printf %o $x)') echo $x;;" ;done
esac
done
و سپس داده باینری را به آن لولهکشی کنید، شاید اینطور :
for x in $(seq 0 255) ;do echo -ne "\\$(printf %o $x)" ;done | bash bindec.bash | nl | less
این کد ایجاب میکند که کاراکتر 0 به طور کلی از قلم انداخته شود، زیرا ما نمیتوانیم آنرا با تولید کننده ورودی ایجاد کنیم، به راحتی برای خراب کردن اکثر فایلهای باینری که میخواهیم پردازش کنیم، کفایت میکند.
بلی، Bash به زبان C نوشته شده، و از معناشناسی این زبان برای مدیریت رشتهها -- شامل بایتهای NUL به عنوان حدفاصل رشتهها -- در متغیرهایش استفاده میکند. شما نمیتوانید به طور معقولی NUL را در متغیرهای Bash ذخیره نمایید. در حقیقت هرگز هم قرار نبوده به این صورت به کار برود. - GreyCat
توجه نمایید که این مطلب اشاره به نگهداری آنها در متغیرها دارد... انتقال دادهها بین برنامهها با استفاده از لولهها همیشه باینری بدون عیب است. فایلهای موقتی نیز به شرطی که موقع ایجاد آنها اقدامات احتیاطی متناسب انجام بشود، بیخطر هستند.
برای cat کردن فایل باینری تنها با دستورات داخلی bash موقعی که برنامه خارجی در دسترس نباشد(یکبار وقتی نام فایل /lib/libgcc_s.so.1 تغییر کرده بود، استفاده از این ترفند کارم را راه انداخت):
# باینری مطمئن bash فقط با دستورات داخلی cat شبیهسازی
IFS=
while read -d '' -r -n1 x ; do
case "$x" in
'') printf "\x00";;
*) printf "%s" "$x";;
esac
done
من ترجیح خواهم داد از cat استفاده کنم. همچنین، آن -n1 واقعاً لازم بود؟ -GreyCat
بدون -n1 شما باید برای کار کردن با دادههای بعد از آخرین \0 خیلی با احتیاط باشید، موردی مانند این [[ $x ]] && printf "%s" "%x" بعد از حلقه. من این را بررسی نکردهام که بدانم آیا کار میکند یا اینکه کافی هست. همچنین من نمیدانم اگر شما یک فایل بزرگ بدون هیچ \0 را بخوانید چه اتفاقی میافتد --pgas
پرسش و پاسخ 58 (آخرین ویرایش 2009-03-09 08:26:26 توسط pgas)
چنان که شخصی بخواهد این فایل را:
foo: entry1
bar: entry2
foo: entry3
baz: entry4
تبدیل کند به این
foo: entry1 entry3
bar: entry2
baz: entry4
دو روش ساده عمومی برای انجام این کار وجود دارد:
یک پیادهسازی اساسی از روش
old=xxx ; stuff=
(sort file ; echo xxx) | while read prefix line ; do
if [[ $prefix = $old ]] ; then
stuff="$stuff $line"
else
echo "$old: $stuff"
old="$prefix"
stuff=
fi
done
و یک پیادهسازی اساسی از روش
{
a[$1,++b[$1]] = $2;
}
END {
for (i in b) {
printf("%s", i);
for (j=1; j<=b[i]; j++) {
printf(" %s", a[i,j]);
}
print "";
}
}
نوشته شده به صورت یک سطر دستور مفصل در پوسته:
awk '{a[$1,++b[$1]]=$2} END {for (i in b) {printf("%s", i); for (j=1; j<=b[i]; j++) printf(" %s", a[i,j]); print ""}}' file
پرسش و پاسخ 57 آخرین ویرایش 2012-03-29 20:36:56 توسط ormaaj)