به طور اساسی پاسخ خیر است....
در حالیکه 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)
چون فرمان tar در اصل طوری طراحی شده بود که از دستگاههای نوار گردان مغناطیسی بخواند یا در آن بنویسد(کلمه tar از
یک گزینه وجود دارد که به tar میگوید آرشیو روی نوار مغناطیسی نیست، بلکه در یک فایل است: -f. این گزینه دقیقاً یک شناسه میپذیرد: نام فایل محتوی آرشیو. تمام نام فایلهای دیگر(در ادامه) به عنوان اعضاء آرشیو در نظر گرفته میشوند:
tar -x -f backup.tar myfile.txt # ( IMHO یا(ترکیب دستوری عمومیتر tar xf backup.tar myfile.txt
حال در اینجا یک اشتباه رایج هست -- تصور کنید دایرکتوری شامل فایلهای بایگانی زیر است و میخواهید همه را به یکباره استخراج کنید:
$ ls backup1.tar backup2.tar backup3.tar
شاید فکر کنید با دستور tar xf *.tar انجام بدهید. بیایید ببینیم:
$ tar xf *.tar tar: backup2.tar: Not found in archive tar: backup3.tar: Not found in archive tar: Error exit delayed from previous errors
جه اتفاقی رخ میدهد؟ پوسته *.tar شما را با فایلهای قابل انطباق تعویض میکند. در حقیقت شما نوشتهاید:
tar xf backup1.tar backup2.tar backup3.tar
و به طوری که فبلاً دیدیم، به معنی آنست که: «فایلهای backup2.tar و backup3.tar را از فایل بایگانی backup1.tar استخراج کن», که البته فقط موقعی موفق خواهد شد که چنین نام فایلهایی در فایل بایگانی ذخیره شده باشند.
راه حل نسبتاً آسان است: استخراج محتویات تمام آرشیوها به طور یکی یکی. چنانچه ما از شل یونیکس استفاده میکنیم و تنبل هستیم، با یک حلقه آن کار را انجام میدهیم:
for tarname in ./*.tar; do tar xf "$tarname" done
چه اتفاقی میافتد؟ حلقه for روی تمام نام فایلها که با *.tar منطبق میشوند تکرار میکند و tar xf را برای هر یک از آنها فرامیخواهد. به این طریق شما تمام فایلهای بایگانی را یک به یک استخراج میکنید و درست آن را به طور خودکار انجام میدهید.
دومین نوع آرشیو رایج در این روزها ZIP است. فرمان unzip برای استخراج محتویات از یک فایل ZIP است(چه کسی آن را پیشنهاد کرده است!). مشکل در اینجا خیلی مشابه است: unzip فقط یک گزینهِ تعیین کننده فایل ZIP را میپذیرد. بنابراین، آن را به همان روش حل کنید:
for zipfile in ./*.zip; do unzip "$zipfile" done
کافی نیست؟ Ok. گزینه دیگری همراه unzip وجود دارد: این گزینه میتواند الگوهای شل-مانند را برای تعیین نام فایلهای ZIP قبول کند. و برای پرهیز از تفسیر آن الگوها توسط پوسته، لازم است آنها را نقلقولی کنید. در این حالت خود unzip و
unzip "*.zip" # :یا برای شفاف کردن آن که چه کار میکنیم unzip \*.zip
(این ویژگی دستور unzip در اصل از خاستگاه آن به عنوان یک برنامه MS-DOS ناشی میگردد. مفسر فرمان MS-DOS بسطهای glob انجام نمیدهد، بنابراین هر برنامه MS-DOS باید قادر باشد فوق کاراکترها را به لیستی از نام فایلها بسط بدهد. این ویژگی در نگارش یونیکس باقی گذاشته شد، و به طوری که نمایش دادیم، گاهی اوقات میتواند مفید باشد.)
پرسش و پاسخ 56 ( 2011-02-11 19:26:45 توسط GreyCat)
تمام پردازشهای Bash به ترتیب از چپ به راست تغییر مسیر داده میشوند. و ترتیب معنا دار است. انحراف از آن در یک فرمان ممکن است نتایج آن فرمان را تغییر بدهد.
اگر تمام آنچه میخواهید ارسال هر دو خروجی استاندارد و خطای استاندارد به یک فایل است، این مورد را به کار ببرید:
# Bourne foo >file 2>&1 # هر دو را به فایل ارسال میکند stdout و stderr
این هم یک نمایش ساده از آنچه اتفاق میافتد:
# POSIX foo() { echo "This is stdout" echo "This is stderr" 1>&2 } foo >/dev/null 2>&1 # هیج خروجی ارائه نمیکند foo 2>&1 >/dev/null # "This is stderr" در نمایشگر مینویسد
چرا نتایج اختلاف دارند؟ در حالت اول،>/dev/null ابتدا انجام میشود، و بنابراین، خروجی استاندارد فرمان به /dev/null فرستاده میشود. سپس، 2>&1 انجام میشود، که باعث میشود استاندارد خطا به همان جایی ارسال شود که خروجی استاندارد از قبل میرفت. بنابراین هر دو خروجی، دور انداخته میشوند.
در مثال دوم، 2>&1 اول انجام میشود. این به معنای آن است که ابتدا خطای استاندارد به جایی ارسال میشود که خروجی استاندارد به آنجا میرود --در این حالت، به ترمینال کاربر. پس از آن، خروجی استاندارد به /dev/null فرستاده میشود و بنابراین دور انداخته میشود. از آن جهت موقعی که ما foo را دفعه دوم اجرا میکنیم، فقط خطای استانداردش را میبینیم، نه خروجی استاندارد آن را.
فصل تغییر مسیر در راهنما شرح میدهد که چرا ما دونسخهای نمودن توصیفگر فایل را به جای دوبار باز کردن /dev/null به کار میبریم. در حالت خاصِ /dev/null واقعاً مسئلهای نیست زیرا تمام نوشتهها رها میشوند، اما موقعی که ما در فایل ثبت وقایع مینویسیم، به راستی خیلی زیاد با اهمیت است.
مواقعی هست که ما به راستی میخواهیم 2>&1 اول ظاهر شود -- برای یک مثال از این مورد، پرسش و پاسخ شماره 40 را ببینید.
مواقع دیگری هست که شاید از 2>&1 بدون هر تغییر مسیر دیگری استفاده کنیم. ملاحظه نمایید:
# Bourne find ... 2>&1 | grep "some error"
در این مثال، میخواهیم خطای استاندارد find را (به علاوه خروجی استاندارد آن)برای رشته "some error" جستجو کنیم. 2>&1 در فرمان لولهکشی شده خطای استاندارد را مجبور میکند همراه با خروجی استاندارد به داخل لوله برود. (موقعی که لولهها و تغییر مسیرها به این طریق مختلط میشوند، به خاطر بیاورید: لوله اول قبل از هر تغییر مسیری انجام میشود. بنابراین خروجی استاندارد find قبلاً برای اشاره کردن به لوله تنظیم گردیدهاست، پیش از اینکه ما تغییر مسیر 2>&1 را پردازش کنیم.)
اگر ما میخواستیم فقط خطای استاندارد را در لوله بخوانیم، و خروجی استاندارد را رها کنیم، میتوانستیم چنین کنیم:
# Bourne find ... 2>&1 >/dev/null | grep "some error"
تغییر مسیرها در این مثال بدین گونه پردازش میشوند:
اول، لوله ایجاد میشود. خروجی find به آن ارسال میشود.
بعد، 2>&1 باعث میشود خطای استاندارد find نیز به لوله برود.
عاقبت، >/dev/null موجب میگردد خروجی استاندارد findدور انداخته شود، فقط خطای استاندارد اجازه رفتن به لوله دارد.
یک پرسش مرتبط پرسش و پاسخ شماره 47 است، که چگونگی ارسال stderr به خط لوله را بحث میکند.
برای یک توضیح بیشتر گرافیکی قابل درک نمودن عملگر کپی توصیفگر فایل را ببینید.
اگر هنوز در مورد این نکته سر در گم هستید، احتمال دارد به علت آن باشد که شما با تصور غلطی در مورد چگونگی کار توصیفگرهای فایل آغاز نمودهاید, و هنوز نمیتوانید آن تصور غلط را رها کنید. نگران نشوید -- این یک موردِ به شدت رایجِ تصور غلط است، و شما تنها نیستید. اجازه بدهید من سعی در تشریح آن بنمایم....
بسیاری اشخاص تصور میکنند که 2>&1 یکجور «به هم پیوستن» یا «گره زدن با هم» یا « ازدواج کردن» دو توصیفگر فایل است، به طوریکه هر تغییر در یکی از آنها تغییری برای دیگری میشود. این حالت نیست. و برای بسیاری افراد، این همان جایی است که گیج شدن از آن حاصل میشود.
2>&1 فقط FD2 را تغییر میدهد تا به "آن جایی که FD1 در آن لحظه به آن اشاره میکند" اشاره نماید، در واقع باعث نمیشود که FD2 به خود FD1 اشاره کند. توجه نمایید که "2" و "1" به سبب روشی که به کار بروند، معانی مختلفی میگیرند: "2"، وقتی قبل از ">&" واقع میشود FD2 واقعی معنی میدهد، اما "1" که بعد از ">&" واقع میشود، به جای خود FD1، به معنی «جایی که FD1 در حال حاضر به آن اشاره میکند» است. (اگر برعکس باشد، همچون "1>&2"، آنوقت 1به معنی خود FD1 میشود، و 2 به معنی «جایی که FD2 در حال حاضر به آن اشاره میکند» خواهد بود.)
شاید قیاسها کمک کنند. یک قیاس در نظر گرفتن FDها به عنوان اشارهگرهای C است.
int some_actual_integer; int *fd1, *fd2; fd1 = &some_actual_integer; /* 1>file قابل مقایسه با*/ fd2 = fd1; /* 2>&1 قابل مقایسه با*/ fd1 = NULL; /* 1>&- قابل مقایسه با*/ /* هنوز به مکان واقعی حافظه اشاره میکند fd2 در این نقطه هر دو باید به یک نقطه اشاره کنند fd2 و fd1 این واقعیت که مناسب نیست. میتوانیم یکی از آنها را ببندیم یا دوباره باز کنیم بدون تأثیر بر دیگری */
یک قیاس دیگر در نظر گرفتن آنها مانند hardlinkها در سیستم فایل است .
touch some_real_file ln some_real_file fd1 # یک لینک به فایل ما ایجاد میکند fd1 ln fd1 fd2 # لینک دیگری به فایل ما ایجاد میکند fd2 rm fd1 # تحت تأثیر قرار نمیگیرد fd2 حذف میشود اما لینک fd1 لینک # "some_real_file":در این نقطه ما بازهم یک فایل با دو لینک داریم # "fd2"و
یا مانند پیوندهای نمادین -- اما با این قیاس باید محتاط باشیم.
touch some_real_file ln -s some_real_file fd1 # را پیوند نمادین از فایل ما میسازد fd1 ln -s "$(readlink fd1)" fd2 # را به همان فایلی پیوند نمادین میسازد که fd2 # .به آن لینک نمادین است fd1 rm fd1 # .دست نخورده میماند fd2 را حذف میکند اما fd1 # .را لازم دارد "readlink" برنامه غیر استاندارد # :نتیجه عبارت است از lrwxrwxrwx 1 wooledg wooledg 14 Mar 25 09:19 fd2 -> some_real_file -rw-r--r-- 1 wooledg wooledg 0 Mar 25 09:19 some_real_file # را استفاده کنیم، به طور "ln -s fd1 fd2" اگر ما سعی میکردیم در این مقایسه # ها نیست، بلکه چگونگی تصورFD نامساعدی شکست میخوردیم. این چگونگی کارکرد # .برخی افراد در باره کار آنها میباشد. و این اشتباه است
مقایسههای دیگر، از جمله تخیل FDها به منزله شیلنگها. با در نظر گرفتن فایلها به عنوان بشکههای پُر آب(یا خالی، یا نیمه پُر). شما میتوانید شیلنگ را در بشکه قرار بدهید، برای آنکه آب بیشتری در آن انباشته شود. میتوانید دو شیلنگ را در یک بشکه قرار بدهید، و آنها هر دو آب را در همان بشکه میریزند. آنوقت شما میتوانید یکی از آن شیلنگها را حذف کنید، و این امر موجب نمیشود شیلنگ دیگر کنار برود. آن یکی بازهم آنجاست.
پرسش و پاسخ 55 (آخرین ویرایش 2011-08-23 17:34:06 توسط S010600032d00065e)
اول، شما باید معین کنید مقصود شما از «عدد» چیست. در اکثر حالتهای رایج که مردم این مورد را سؤال میکنند، به نظر میرسد منظور «یک عدد صحیح غیر منفی، بدون علامت + » است. یا به بیان دیگر رشتهای از تمام ارقام. سایر اوقات، افراد میخواهند یک ورودی ممیز شناورِ با علامت و نقطه اعشار اختیاری را تعیین اعتبار نمایند.
اگر شما در حال اعتبار سنجی ساده «رشتهای از ارقام» میباشید، میتوانید آن را با glob انجام بدهید:
# Bash if [[ $foo != *[!0-9]* ]]; then echo "'$foo' is strictly numeric" else echo "'$foo' has a non-digit somewhere in it" fi
همان کار با کاربرد case میتواند در پوستههای Korn و POSIX به خوبی انجام شود:
# ksh, POSIX case "$foo" in *[!0-9]*) echo "'$foo' has a non-digit somewhere in it" ;; *) echo "'$foo' is strictly numeric" ;; esac
اگر به مجاز نمودن یک علامت منفی مقدم، یا اگر به عدد معتبر با ممیز شناور یا مورد دیگر پیچیدهتری نیاز دارید، آنوقت چند روشِ ممکن موجود است. globهای استاندارد به اندازه کافی برای این کار گویا نیستند، اما میتوانیم از globهای توسعهیافته استفاده کنیم:
# Bash --های توسعه یافته باید فعال شوندglob # .کنترل میکند که آیا متغیر تماماً متشکل از ارقام است shopt -s extglob [[ $var == +([0-9]) ]]
یک حالت پیچیدهتر:
# Bash shopt -s extglob [[ $foo = *[0-9]* && $foo = ?([+-])*([0-9])?(.*([0-9])) ]] && echo "foo is a floating-point number"
تست مقدم بر $foo برای اطمینان از وجود حداقل یک رقم میباشد. glob توسعهیافته، به تنهایی رشته تهی، یا + یا - تنها را میتواند مطابقت کند، که شاید رفتار مطلوب نباشد.
پوسته Korn به طور پیشفرض globهای توسعهیافته فعال دارد، اما فاقد [[ است، بنابراین برای انجام انطباق جانشین باید از case استفاده کنیم:
# Korn case $foo in *[0-9]*) case $foo in ?([+-])*([0-9])?(.*([0-9]))) echo "foo is a number";; esac;; esac
توجه نمایید که این کُد از همان glob توسعهیافته استفاده میکند که Bash در مثال قبل به کار برد، سومین پرانتز بسته، در انتهای آن در حقیقت بخشی از ترکیب دستوری case میباشد.
اگر تعریف شما از «عدد معتبر» حتی پیچیدهتر است، یا اگر نیاز به راه حلی دارید که در پوستههای موروثی Bourne کار کند، شاید ترجیح بدهید از ترکیب دستوری عبارت منظمِ یک ابزار خارجی استفاده کنید. نگارش قابل حمل(که در اینجا به طور تفصیلی تشریح شده)، با استفاده از egrep:
# Bourne if echo "$foo" | egrep '^[-+]?([0-9]+\.?|[0-9]*\.[0-9]+)$' >/dev/null then echo "'$foo' is a number" else echo "'$foo' is not a number" fi
Bash نگارش 3 و بالاتر در فرمان [[ دارای پشتیبانی از عبارت منظم میباشد. به سبب باگها و تغییرات در پیادهسازی ویژگی =~ در bash 3.x، ما استفاده از آن را پیشنهاد نمیکنیم، اما در هر صورت افراد از آن استفاده میکنند، بنابراین ما باید در اینجا این مثال را حفظ کنیم (و این هشدار بازدارنده را نیز نگاه داریم، تا وقتی مردم آن را حذف کنند):
# Bash # عبارت منظم را در متغیر قرارمیدهیم <3.2 برای سازگاری معکوس با نگارشهای regexp='^[-+]?[0-9]*(\.[0-9]*)?$' if [[ $foo = *[0-9]* && $foo =~ $regexp ]]; then echo "'$foo' looks rather like a number" else echo "'$foo' doesn't look particularly numeric to me" fi
# ناموفق است ksh با if [ "$foo" -eq "$foo" ] 2>/dev/null;then echo "$foo is an integer" fi
[ به علت وجود -eq متغیر را تفکیک میکند و آن را به عنوان عدد صحیح تفسیر مینماید. اگر تفکیک موفق باشد به طور بدیهی بررسی صحیح است، اگر ناموفق باشد [ یک پیغام خطا چاپ میکند که 2>/dev/null آنرا پنهان میکند و یک کد وضعیت غیر صفر را تنظیم میکند. به هر حال این شیوه در پوسته ksh ناموفق میشود، زیرا ksh متغیر را به عنوان یک عبارت حسابی ارزیابی میکند.
میتوانید از ترفند مشابهی با printf استفاده کنید:
# POSIX if printf "%f" "$foo" >/dev/null 2>&1; then echo "$foo is a float" fi
میتوانید از %d برای تجزیه یک عدد صحیح استفاده کنید. مراقب باشید که تجزیه میتواند وابسته به منطقه باشد(آیا تصور میرود که باشد؟).
if (("$foo")) >/dev/null 2>&1; then echo "$foo is an integer" fi
دستور (($foo)) سعی میکند foo را به عنوان یک عبارت حسابی صحیح تعیین کند، که اگر foo یک عدد صحیح نباشد یک کد خطای غیر صفر برمیگرداند.
پرسش و پاسخ 54 (آخرین ویرایش 2012-12-02 11:27:59 توسط geirha)