چون فرمان 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)
شما باید \[ و \] را در اطراف هر یک از رشتههای escape غیرقابل چاپ در اعلان خود قرار بدهید. از این قرار:
fancy_prompt() {
local blue=$(tput setaf 4)
local purple=$(tput setaf 5)
local reset=$(tput sgr0)
PS1="\[$blue\]\h:\[$purple\]\w\[$reset\]\\$ "
}
بدون \[ \]، bash گمان خواهد کرد بایت هایی که رشتههای escape برای کُدهای رنگ را تشکیل میدهند به طور واقعی فضایی را در نمایشگر اشغال خواهند نمود، بنابراین bash قادر نخواهد بود بداند که اشارهگر واقعاً کجا میباشد.
اگر شما بازهم مشکل دارید، به عنوان مثال، موقعی که تاریخچه فرمانها را با کلیدهای جهتی بالا و پایین مرور میکنید، اطمینان حاصل نمایید که گزینه checkwinsize تنظیم باشد:
shopt -s checkwinsize
به فصلANSI escape codes ویکی پدیا مراجعه نمایید.
به طور کلیتر، شما باید از نوشتن رشتههای escape ترمینال به طور مستقیم در اعلان خود اجتناب نمایید، زیرا آنها لزوماً در میان تمام ترمینالهایی که اکنون یا در آینده استفاده خواهید نمود قابل حمل نیستند. از tput برای تولید رشتههای صحیح برای ترمینال خود استفاده کنید( این برنامه به بانک اطلاعاتی terminfo یا termcap شما نگاه میکند).
چون tput یک فرمان خارجی است، هر چند مرتبه که لازم باشد، شما باید آن را اجرا کنید، به این علت پیشنهاد میکنیم نتایج آن را در متغیرها ذخیره کنید، و (به جای استفاده از $(tput ...) به طور مستقیم در PS1، که در هر نوبتی که اعلان نمایش داده میشود tput را اجرا میکند)، آنها را در ساختار اعلان به کار ببرید. به این ترتیب کُد تشکیل دهنده اعلان برای خواندن آسانتر از خود اعلان میباشد، و درطیف متنوعی از ترمینالها کار میکند. (بعضی ترمینالها شاید ویژگیهایی که سعی میکنید به کار ببرید را نداشته باشند ، از قبیل رنگها، بنابراین نتایج در وضعیتهای پیچیده به اندازه 100% قابل حمل نیستند. اما میتوانید نزدیک شوید.)
یادداشت شخصی: بازهم من این جواب را ترجیح میدهم:
BLUE=$(tput setaf 4) PURPLE=$(tput setaf 5) RESET=$(tput sgr0) PS1='\[$BLUE\]\h:\[$PURPLE\]\w\[$RESET\]\$ '
من درک میکنم که اشخاص میخواهند از مخدوش شدن متغیر namespace زیرنویس 1 اجتناب کنند، بنابراین از تابع و بخش local استفاده میکنند، که به ترتیب استفاده از نقلقول دوگانه و نیاز به دوتایی نمودن بعضی از
imadev:~$ FOO='\w'; PS1='$FOO\$ ' \w$ FOO='\w'; PS1="$FOO\\$ " ~$
فرض کنیم ترمینال ما از \w در یک رشته escape استفاده میکند. یک \w داخل متغیری که در PS1 نقلقولی شده منفرد رجوع میشود، وقتی اعلان چاپ گردد فقط به \w لفظی بسط داده میشود، که آنچه ما میخواهیم است. اما در نگارش نقلفول دوگانه ، \w که به طور مستقیم داخل متغیر PS1 گمارده شده، موقع چاپ اعلان، توسط bash ارزیابی میشود. حال، من واقعاً ترمینالی را که از چنین نشانهای استفاده کند نمیشناسم --این یک ایراد کاملاً نظری است. اما از طرف دیگر، ایراد وارده به استفاده از متغیرهایی مانند BLUE نیز همینطور است. و به هر حال برخی اشخاص شاید واقعاً بخواهند در پوستههایشان از echo "$BLUE" استفاده کنند. بنابراین من نمیخواهم بگویم پاسخ نقلقول منفرد بهتر است، بلکه دوست داشتم ببینم که به عنوان یک جایگزین در اینجا حفظ میشود. -- GreyCat
کاملاً صحیح. من در ابتدا قصد داشتم فقط BLACK= را به RESET= تغییر بدهم(چون همه از سفید روی سیاه استفاده نمیکنند)، اما بعد اندیشیدم اگر اعلان به متغیرهایی که متغیر هستند وابسته نباشد، بهتر خواهد شد. من به طور واضح در مورد احتمال چنین رشتههای escape ترمینال آگاه نبودم، بنابراین فکر میکنم ذکر کردن نگارش اول نقلقول منفرد، میتوانست ایده بهتری باشد و همچنین یادآوری آنکه اگر آن متغیرها تغییر کنند چه اتفاقی میافتد.
من گمان میکنم شخص میتوانست برای پیشگیری از تغییر تصادفی متغیرها و ضایع شدن اعلان فرمان، آنها را فقط خواندنی نیز بنماید، اگر چه احتمالاً آن نیز اشکالات دیگری خواهد داشت..؟
پرسش و پاسخ 53 (آخرین ویرایش 2012-03-27 20:46:02 توسط GreyCat)
در اینجا تداخل کلمات به کار رفته برای رنگها با نام متغیرها، مورد نظر GreyCat است.
(1)
در برخی سیستمها برای علامت زدن انتهای سطر، کاراکترهای رفتن سر سطر(CR)به کار میروند. سه نوع مختلف انتهای سطر رایج است:
اگر شما در حال اجرای اسکریپتی در سیستم یونیکس میباشید، لازم است انتهای سطرها یونیکسی باشد(فقط LFها)، در غیر آنصورت مشکل خواهید داشت.
یک بازرسی ساده، فقط نگاه کردن به خروجی cat -e است:
cat -e yourscript
اگر چیزی مانند این را میبینید، آنوقت با سطرهای جدید سَبکِ CRLF سر و کار دارید:
command^M$ ^M$ another command^M$
یک شیوه دیگر، استفاده از برنامه سودمند file برای پی بردن به نوع فایل است:
file yourscript
اگر فایل دارای CR باشد خروجی به شما میگوید که متن ASCII داری CR میباشد. توجه: این روش فقط در گنو-لینوکس صادق است. در سایر سیستمعاملها، نتیجه file غیرقابل پیشبینی است، به استثنای آنکه اگر نوع فایل مانند فایل متن باشد کلمه "text" در جایی از نتیجه خروجی ظاهر میشود.
imadev:~$ printf 'DOS\r\nline endings\r\n' > foo imadev:~$ file foo foo: commands text arc3:~$ file foo foo: ASCII text, with CRLF line terminators
در یک اسکریپت، گفتن آنکه کدام شیوه، قابل اعتمادترین روش خواهد بود، مشکلتر است. هر کدام که انجام بدهید، غیر مستدل خواهد بود. در تئوری، یک فایل غیر معیوب تولید شده توسط برنامه سودمند یونیکسی به سامان، تنها باید شامل LFها، و تولید شده توسط یک برنامه DOS، نباید شامل LF های ساده بدون یک CR مقدم، باشد. میتوانید این مورد را با یک PCRE بررسی کنید.
# Bash و Ksh در پوسته
# [[ $(</dev/fd/0) == ~(P)(?<!$'\r')$'\n' ]] # ksh93 PCRE
pattern=*[^$'\r']$'\n'*
if [[ $(</dev/fd/0) == $pattern ]] <<<$'foo\r\nbar\r\nbaz\r'; then
print 'File contains only CRLFs'
else
print 'File contains at least one newline not preceded by a CR'
fi
ex یک روش استاندارد مناسب برای تبدیل CRLF به LF میباشد، و احتمالاً یکی از چند روش قابل اعتماد انجام آن به طور یکجا از داخل یک اسکریپت است:
ex -sc $'%s/\r$//e|x' file
البته، هر یک از زبانهای پویای قدرتمندتر برای انجام این کار با سهولت نسبی.
perl -pi -e 's/\r\n/\n/' filename
برخی سیستمها دارای ابزارهای معتبر ویژه تبدیل، جهت انجام این کار به طور خودکار، هستند. dos2unix، recode، و fromdos برخی نمونهها میباشند.
به طور دستی با ویرایشگری مانند nano انجام میشود:
nano -w yourscript
Ctrl-O را تایپ کنید و قبل از تأیید، Alt-D (DOS) یا Alt-M (Mac) را برای تغییر قالب تایپ نمایید.
یا در Vim از :set fileformat=unix استفاده کنید و با :w ذخیره کنید. مراقبت کنید که مقدار fenc صحیح باشد (احتمالاً utf-8).
برای آنکه به سادگی تمام CRها را از جریان ورودی پاک کنید، میتوانید از tr -d '\r' <infile >outfile استفاده نمایید. البته، شما باید مطمئن باشید که نام این فایلها یکسان نباشد.
پرسش و پاسخ 52 (آخرین ویرایش 2013-02-10 15:48:58 توسط ormaaj)