به نظر نمیرسد هیچ فرمان منفردی وجود داشته باشد که واقعاً در همه جا کار بکند. 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)