به نظر نمیرسد هیچ فرمان منفردی وجود داشته باشد که واقعاً در همه جا کار بکند. tempfile قابل حمل نیست. mktemp به طور وسیعتری موجود است( اما باز هم به طور در همه جا حاضر نیست)، فقط ممکن است برای ایجاد فایل از قبل، مستلزم گزینه -c باشد، یا ممکن است به طور پیشفرض فایل را ایجاد کند و اگر -c فراهم شده باشد، ناموفق گردد. برخی سیستمها هیچ یک از دو فرمان را ندارند(سولاریس و POSIX).
پاسخ سنتی به طور معمول چیزی مانند این بوده:
# استفاده نکنید! وضعیت مسابقه! tempfile=/tmp/myname.$$ trap 'rm -f "$tempfile"; exit 1' 1 2 3 15 rm -f "$tempfile" touch "$tempfile"
مشکل با این مورد عبارت است از: اگر فایل از قبل موجود باشد(برای مثال، به عنوان یک لینک به /etc/passwd)، آنوقت ممکن است اسکریپت مواردی را در محلهایی بنویسد که نباید نوشته شوند. حتی اگر شما فایل را فوراً قبل از استفاده از آن حذف کنید،، بازهم یکوضعیت مسابقه دارید: شخصی میتواند در فاصله مابین دستورات پوسته شما یک لینک بداندیشانه بازتولید نماید.
بهترین پاسخ قابل حمل قرار دادن فایلهای موقتی در دایرکتوری خانگی خودتان (یا بعضی دایرکتوریهای شخصی دیگر) است، جایی که هیچ فرد دیگری مجوز نوشتن ندارد. آنوقت لااقل نگرانی در مورد کاربران بداندیش را ندارید. طرحهای ساده مبتنی بر PID (یا نام میزبان + PID برای سیستم فایلهای به اشتراک گذاشته شده) جهت پیشگیری از تداخل با اسکریپتهای خودتان، کفایت خواهد نمود.
متأسفانه، به نظر نمیرسد افراد این پاسخ را دوست داشته باشند. آنها خواستار آن هستند که فایلهای موقتیشان در /tmp یا /var/tmp باشند. برای آن افراد، روش پاکیزهای وجود ندارد، بنابراین آنها باید روش هوشمندانه(hack)ای که میتوانند با آن ادامه بدهند را انتخاب کنند.
در برخی سیستمها(مانند لینوکس):
شما دارای فرمان معتبر mktemp میباشید و میتوانید از گزینه -d آن استفاده نمایید، برای اینکه دایرکتوریهای موقتی ایجاد میکند که تنها برای شما قابل دستیابی است، همراه با کاراکترهای تصادفی در نام آنها جهت تقریباً غیر ممکن نمودن حدس زدن پیشاپیشن نام دایرکتوری برای یک مهاجم.
میتوانید فایلهایی با نام طولانیتر از ۱۴ کاراکتر در /tmp ایجاد کنید.
# POSIX در # تنظیم میکند "/tmp" از قبل دارای مقدار نباشد آن را به $TMPDIR فقط در صورتیکه : ${TMPDIR:=/tmp} # ببینیم http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html این صفحه را $TMPDIR میتوانیم در باره # ایجاد کنید $TMPDIR یک دایرکتوری موقتی خصوصی در # وقتی اسکریپت خاتمه مییابد دایرکتوری موقتی را حذف کنید unset temporary_dir trap '[ "$temporary_dir" ] && rm -rf "$temporary_dir"' EXIT save_mask=$(umask) umask 077 temporary_dir=$(mktemp -d "$TMPDIR/XXXXXXXXXXXXXXXXXXXXXXXXXXXXX") || { echo "ERROR creating a temporary file" >&2; exit 1; } umask "$save_mask"
و آنوقت میتوانید فایلهای ویژه خود را در دایرکتوری موقتی ایجاد کنید.
یک پیشنهاد متفاوت که به فرمان mktemp نیاز ندارد: با استفاده از متغیر RANDOM به صورت زیر، یک دایرکتوری موقتی که بعید است با نام یک دایرکتوری موجود مطابقت بنماید، میتواند ایجاد گردد:
temp_dir=/tmp/$RANDOM mkdir "$temp_dir"
این کد یک دایرکتوری به شکل /tmp/20445/ ایجاد خواهد نمود. برای کاهش احتمال برخورد با نام یک دایرکتوری موجود، متغیر RANDOM میتواند چندبار به کار برود، یا با ID پردازش ترکیب بشود:
temp_dir=/tmp/$RANDOM-$$ mkdir -m 700 "$temp_dir" || { echo "failed to create temp_dir" >&2; exit 1; }
این کد یک دایرکتوری به صورت /tmp/24953-2875/ ایجاد خواهد نمود. این مورد از یک موقعیت مسابقه اجتناب میکند، به علت اینکه mkdir همانطور که در پرسش و پاسخ ۴۵ میبینیم، تجزیه ناپذیر(اتمی) است. لازمالاجراست که شما نتیجهmkdir را کنترل نمایید، در صورتی که در ایجاد دایرکتوری ناموفق باشد ، واضح است که نمیتوانید پیش بروید.
(برخی سیستمها دارای محدودیت ۱۴ کاراکتر در نام فایل هستند، بنابراین از آزمایش ترکیب بیش از دوبارِ رشته $RANDOM همراه یکدیگر اجتناب کنید. شما برای امنیت خود به تجزیه ناپذیری mkdir استناد میکنید، نه برای گمنامی نام تصادفی انتخابی. اگر شخصی دایرکتوری /tmp را با صدهاهزار فایلهای دارای نام عدد تصادفی به منظور انسداد راه شما، پُر کند، با مسایل بزرگتری مواجه هستید.)
به جای RANDOM، میتوان از awk برای تولید عدد تصادفی به یک روش سازگار با POSIX استفاده نمود:
temp_dir=/tmp/$(awk 'BEGIN { srand (); print rand() }') mkdir -m 700 "$temp_dir"
توجه کنید، به هر حال، آن srand() سرچشمهِ تولید عدد تصادفی، از ثانیهها نسبت به epoch زیرنویس 1 استفاده میکند که پیش بینی کردن آن برای یک متخاصم و انجام حمله پردهپوشی سرویس(DoS)، به طور مساعدی آسان است.
با ترکیب فوق میتوانیم به کار ببریم:
temp_dir=/tmp/"$(awk -v p=$$ 'BEGIN { srand(); s = rand(); sub(/^0./, "", s); printf("%X_%X", p, s) }')" mkdir -m 700 "$temp_dir" || { echo '!! unable to create a tempdir' >&2; exit 1; }
چون در awk قالب عددی پیشفرض %g میباشد که معادل شش رقم بعد از نقطه اعشار است، ما یک دنباله شش رقمی داریم. 999,999 به صورت هگز میشود F423F که یک کاراکتر به ما پس میدهد، یکی دیگر برای _ و 8 کاراکتر برای پیشوند نمودن pid در سیستم فایل ۱۴ کاراکتری در دسترس است. یک شماره شناسایی پردازش(pid) سی و دو بیتی بدون علامت، حداکثر ۸ رقم هگز خواهد شد، بنابراین مورد فوق در اکثر مکانهایی که ممکن است شخصی با چنین سیستم فایلی روبرو گردد، کار میکند. و البته چنانکه greycat اشاره نمود، موقع وجود کمبودها، از تنگنا رها میشویم.
آیا این یک مقداری غیر منطقی نمیباشد؟ -- GreyCat
نه چندان بیش از رویکرد غیرقابل حمل ارائه شده فوق. نقطه نظر مکرر شما در باره محدودیت ۱۴ کاراکتری در سیستمفایلها را جواب میدهد، و استفاده از /tmp که برخی مدیران سیستم ترجیح میدهند را میسر میسازد، و در صورتی که اسکریپت یک برنامه تعریف شده با یک نام خاص نباشد، میتواند سودمند واقع گردد، در آن حالت من موافق هستم که داشتن یک دایرکتوری معین تحت $HOME یا جای دیگر، رویکرد بهتری میباشد. به عنوان یک موضوع فرعی، پیشوند pid درجهای از کنترل یا آگاهی بر هر دایرکتوری موقتی ایجاد شده را فعال میکند. شخصاً ترجیح میدهم این مورد را در ترکیب با فراخوانهای trap و استفاده از $TMPDIR ، که برای POSIX sh قابل حمل است را داشته باشم تا فراخوانی mktemp که من نمیتوانم به آن تکیه کنم. -- igli
من تصریح میکنم که بهترین راهحل برای نگهداری فایلهای اختصاصی شما استفاده از دایرکتوری $HOME شما میباشد. اگر شما در حال پیادهسازی یک سرویس کمکی یا موردی هستید که تحت یک حساب کاربری اجرا میشود، چرا به سادگی یک دایرکتوری اختصاصی برای آن سرویس در همان زمانی که آن کد را نصب میکنید، نسازید؟
یک پیشنهاد نه کاملاً جدی دیگر پیوست کردن کُد C در اسکریپتی است که فرمان mktemp(1) را برمبنای تابع کتابخانهای mktemp(3) پیادهسازی میکند. اما این کار دو مشکل دارد:
پرسش و پاسخ 62 (آخرین ویرایش 2013-01-11 04:00:34 توسط 82)
در اینجا پیوندهایی به مستندات رسمی 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)
یک race condition وضعیتی است که در آن دو یا چند مورد به طور همزمان در حال وقوع میباشند، و نتیجه نهایی به زمانبندی دقیق رویدادها بستگی دارد.
برای مثال، دو برنامه را در نظر بگیرید که در یک زمان اجرا میشوند:
#!/bin/sh # برنامه اول read number < file number=$(($number + 1)) echo $number > file
#!/bin/sh # برنامه دوم read number < file number=$(($number - 1)) echo $number > file
برنامه اول، یک عدد را از فایل میخواند، در حافظه یکی به آن اضافه میکند، و آنوقت جواب را در فایل مینویسد. برنامه دوم عدد را میخواند، یک را از آن کم میکند، و سپس پاسخ را در فایل مینویسد. فرض کنید ما عدد 42 را در فایل بگذاریم و هر دو برنامه را در یک زمان اجرا کنیم. چه اتفاقی رخ میدهد؟
به طور سادهانگارانه، انتظار خواهیم داشت مقدار نهایی در فایل 42 بشود. اگر سیستم عامل ابتدا برنامه اول را زمانبندی کند، آنوقت آن برنامه 43 را در فایل مینویسد، سپس برنامه دوم 43 را میخواند، یک را از آن کم میکند و 42 را در فایل مینویسد. همچنین، اگر سیستمعامل ابتدا برنامه دوم را در زمانبندی قرار دهد، انتظار خواهیم داشت فایل محتوی 41 و سپس موقعی که برنامه اول تمام میشود، 42 بشود. درست است؟
خوب، به طور حتم این یک پیامد محتمل میباشد. اما تنها نتیجه ممکن نیست، به علت اینکه برنامهها atomic نیستند -- یعنی، آنها تمام مراحل خود را بدون انقطاع انجام نمیدهند.
یک توالی احتمالی دیگر وقوع رویدادها چنین است:
برنامه دوم 41 را در فایل مینویسد. نتیجه نهایی: 41
اگر دو سطر انتهایی به طور برعکس رخ بدهند، آنوقت جواب نهایی 43 خواهد شد. بنابراین، به نسبت تمایل زمان بندی سیستمعامل، میتوانیم نتایج انتهایی 41, 42 یا 43 را داشته باشیم.
وضعیت مسابقه در اَشکال بسیاری ظاهر میشود. در چندین نوع برنامهنویسی آنها موضوع در خورِ رسیدگی هستند. هر وقت باید کنترل کنندههای ناهماهنگ نوشته شوند، یا در یک سیستمعامل چند کاربره، با سیستمعامل فعل و انفعال صورت بگیرد، برای اجتناب از وضعیت مسابقه، باید مراقبت فوقالعاده انجام بشود.
این به طور بالقوه یک فرمان خطرناک است. آن را اجرا نکنید! راهاندازی از پرسش فوق حذف میشود، تنها آن قسمتی که تابع را تنظیم میکند باقی میماند.
بمب خوشهای یک شکل ساده تکذیب سرویس(یا حمله 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)