حال که میتوانید بدون دردسر توصیفگرهای فایل را برای هدایت انواع معینی از خروجیها به فایلهای معین، مدیریت نمایید، وقت آنست که رموز مبتکرانهتری که برای تغییرمسیر ورودی و خروجی در دسترس میباشد را بیاموزید.
میتوانید از تغییرمسیرفایل برای نوشتن خروجی در فایلها یا خواندن ورودی از فایلها، استفاده کنید. اما اگر بخواهید خروجی یک برنامه را به طور مستقیم به ورودی برنامه دیگر مربوط کنید چطور؟ از آن طریق میتوانید زنجیر پیوستهای از پردازش خروجی ایجاد کنید. اگر از قبل درباره
$ ls $ mkfifomyfifo ; lsmyfifo $ grepbea myfifo & [1] 32635 $ echo "rat > cow > deer > bear > snake"> myfifo bear
از دستور mkfifo برای ایجاد یک فایل جدید به نام 'myfifo' در دایرکتوری جاری استفاده کردیم. این یک فایل معمولی نیست، بلکه یک
در مثال ما،
اما این فایلهای موقتی یک اذیت واقعی هستند. شاید شما مجوز نوشتن نداشتهباشید. لازم است به خاطر داشته باشید که فایلهای موقتی که ایجاد نمودهاید را پاک کنید. لازم است مطمئن شوید که دادهها وارد و خارج میشوند، یا شاید
به خاطر این مسائل، ویژگی دیگری در دسترس قرارگرفته است: لولهها. در اصل، لوله فقط
$ echo "rat > cow > deer > bear > snake"| grepbea bear
لوله با استفاده از عملگر
لولهها به طور وسیعی به عنوان پسپردازش خروجی برنامهها به کار میروند. به
عملگر لوله برای هر دستور یک محیط پوسته فرعی ایجاد میکند. آگاهی از این مطلب اهمیت دارد، به علت آنکه، هر متغیری که در دستور دوم تعیین یا ویرایش کنید، در خارج از آن به صورت ویرایش نشده قبلی ظاهر میگردد. اجازه بدهید تشریح نمایم:
$message = Test $ echo 'Salut, le monde!'| readmessage $ echo "The message is:$ message "The message is: Test $ echo 'Salut, le monde!'| { readmessage ; echo "The message is: $message";} The message is: Salut, le monde! $ echo "The message is:$ message "The message is: Test
وقتی خط لوله به پایان میرسد، پوستههای فرعی که برای آن ایجاد شدهاند نیز خاتمه مییابند. همراه با پوستههای فرعی، هر تغییر و تبدیل انجام شده در آنها نیز از بین میرود. بنابراین مراقب باشید!
تکرار مفید:
لولهها به عنوان پسپردازشگر خروجی برنامهها، خیلی جالب هستند. به هرحال، شما بایدمراقب باشید در کاربرد آنها زیادهروی نکنید. اگر شما خطلولهای که شامل سه برنامه یا بیشتر باشد را به انتها برسانید، وقت آنست که از خود بپرسید آیا در حال انجام امور به روش هوشمندانهای هستید؟ امکان دارد قادر باشید ویژگیهای بیشتری از برنامه را نسبت به آنچه در یک پسپردازش در لوله به کار گرفتهاید، استفاده کنید. هر دستور جدید در یک خطلوله موجب یک پوسته فرعی جدید میگردد و یک برنامه جدید باید بارگزاری شود. همچنین این مطلب دنبال کردن منطق اسکریپت شما را دشوار میسازد!
من متغیرهایی را در یک حلقه مقرر میکنم. چرا آنها پس از اتمام حلقه، ناگهان ناپدید میگردند؟ یا، چرا نمیتوانم دادهها را برای خواندن لولهکشی نمایم؟
چطور میتوان دو پردازش را با استفاده از لولههای بانام (fifoها)به یکدیگر ارتباط داد؟
گاهی نگهداری دادهها در یک فایل زائد است. ممکن است فقط مقدار بسیارکمی داشته باشیم -- مناسب برای به راحتی گنجاندن آن در خود اسکریپت .یا ممکن است خواسته باشیم محتوای متغیری را ، بدون آنکه اول آن را در یک فایل بنویسیم، به یک دستور تغییر مسیر بدهیم.
$ grep proud<< END > I am a proud sentence. >END I am a proud sentence.
این یک Heredoc (یا سند اینجا) میباشد. وقتی میخواهید یک قطعه کوچک چند سطری دادهها را در اسکریپت خود تعبیه کنید Heredocها سودمند هستند. (تعبیه قطعات بزرگ تکنیک نامناسبی است. شما باید منطق خود(کد خودتان) و ورودی خود(دادههایتان) را جداگانه و ترجیحاً در فایلهای مختلف نگاه دارید، مگر اینکه دادهها جزئی باشند.)
در یک Heredoc، کلمهای برای ایفای نقش نگهبان انتخاب میکنیم. هر کلمهای میتواند باشد، ما از
چند مورد متمایز برای Heredocها وجود دارد. به طور معمول، نمیتوانید آنها را دندانهدار کنید، همه فاصلههایی که در اینجا برای دندانهدار کردن اسکریپت به کار ببرید در
echo "Let's test abc:"if [[ abc = a * ]]; then cat<< END abc seems to start with an a!END fi
چنین نتیجه خواهد داد:
Let's test abc: abc seems to start with an a!
میتوانید با حذف موقتی توگذاری برای سطرهای Heredocهای خود، از این مطلب اجتناب کنید. به هر حال، موجب بدشکل شدن تورفتگی شکیل و زیبای شما میگردد. یک جایگزین وجود دارد. اگر از
به طور پیشفرض، جایگزینیهای BASH در محتوای Heredoc انجام میشود. هر چند، اگر کلمهای که برای جداکردن Heredoc خود به کار میبرید را نقلقولی نمایید، BASH هیچگونه جایگزینی در محتویات انجام نخواهد داد. برای دیدن تفاوت، این مثال را با و بدون کاراکترهای نقلقول امتحان کنید:
$ cat<<' XYZ ' > My home directory is$ HOME >XYZ My home directory is $HOME
رایجترین کاربرد Heredocها، در ارائه مستندات به کاربر است:
usage() { cat<< EOF usage: foobar [-x] [-v] [-z] [file ...] A short explanation of the operation goes here. It might be a few lines long, but shouldn't be excessive.EOF }
حالا اجازه بدهید Herestring خیلی مشابه اما فشردهتر را آزمایش کنیم:
$ grepproud <<< "I am a proud sentence"I am a proud sentence.
ایندفعه،
$ grepproud <<< "$USER sits proudly on his throne in$ HOSTNAME ."lhunath sits proudly on his throne in Lyndir.
Herestringها کوتاهتر، کمتر مزاحم، و به طور کلی مناسبتر از Heredocهای حجیم همتای خود میباشند. گرچه آنها قابل حمل به پوسته Bourne نیستند.
بعداً، شما در باره لولهها و اینکه چطور میتوانند برای ارسال خروجی یک دستور به
$ echo 'Wrap this silly sentence.'| fmt -t -w 20Wrap this silly sentence. $ fmt-t -w 20<<< 'Wrap this silly sentence.'Wrap this silly sentence.
heredocهای بلند به طور معمول ایده نامناسبی هستند، زیرا اسکریپتها باید محتوی منطق باشند، نه دادهها. اگر اسکریپت به سند حجیمی نیاز دارد، باید آن را در یک فایل جداگانه با اسکریپت همراه کنید. با این وجود Herestringها، اغلب کاملاً سودمند هستند بویژه برای ارسال محتوای یک متغیر(به جای فایلها) به فیلترهایی مانند grep یا sed.
در مستندات گنو: Here Documents, Here Strings
حال که دانستید چگونه پردازش ورودی و خروجی را با فرستادن به فایلها یا دریافت از آنها اداره کنید، اجازه دهید باز هم کمی آن را جذابتر نماییم.
همانطور که میدانید، تغییر منبع یا مقصد توصیفگرهایفایل برای اشاره به فایلها یا از آنها، امکان پذیر است. همچنین کپی کردن یک FD به دیگری ممکن است. اجازه دهید بستر آزمایش سادهای فراهم کنیم:
$ echo "I am a proud sentence."> file
فایلی به نام
برنامه کاربردی به نام grep وجود دارد که به طور مختصر در فصل قبل دیدهایم. grep شبیه یک نوار لولهای است: میتوانید تقریباً در هر پروژهای آن را به کار ببرید(خواه ایده خوبی باشد یا نباشد). اساساً یک الگوی جستجو را به عنوان یک شناسه دریافت میکند و میتواند چند نام فایل نیز به عنوان شناسه اضافی دریافت نماید. درست مانند cat، برنامه grep نیز اگر نام فایلی را به عنوان شناسه تعیین نکنید از
$ ls house/drawer closet dustbin sofa $ grep-r socks house/house/sofa:socks
در این مثال تخیلی، دایرکتوری به نام house با چند قطعه اثاثیه منزل در آن به عنوان فایل داریم. اگر ما در جستجوی socks(ساکها)ی خود در هر یک از آن فایلها باشیم، grep را به جستجو در دایرکتوری house/ میفرستیم. grep هر چیزی در آنجا را جستجو میکند، هر فایل را باز میکند و محتویات آن را نگاه میکند. در مثال ما، grep ساکها( socks) را در فایل house/sofa پیدا میکند،احتمالاً پنهان شده زیر یک بالش. مثال واقعبینانهتری میخواهید؟ حتماً:
$ grep "$ HOSTNAME " /etc/*/etc/hosts:127.0.0.1 localhost Lyndir
اینجا به grep دستور دادهایم هر آنچه
خوب، حال که grep را دریافتید, اجازه بدهید با دستکاری توصیفگرفایل ادامه دهیم. به خاطر میآورید که فایلی به نام
$ grep proud *file:I am a proud sentence.
صحیح! grep جمله ما را در
$ grep proud file 'not a file'file:I am a proud sentence. grep: not a file: No such file or directory
این دفعه، به grep دستور دادهایم رشته proud را درفایلهای '
حال، چطور میتوان این جمله grep را به طور کامل خاموش کنیم؟ میتوانیم تمام خروجی که روی ترمینال ظاهر میشود را به جای آن به یک فایل بفرستیم، اجازه بدهید آن را
# Not quite right.... $ grep proud file 'not a file'> proud.log 2 > proud.log
صحیح به نظر میآید؟ ابتدا از
$ echo "I am a very proud sentence with a lot of words in it, all for you."> file2 $ grep proud file2 'not a file'> proud.log 2 > proud.log $ catproud.log grep: not a file: No such file or directory of words in it, all for you.
در اینجا چه اتفاقی رخ داده؟ grep اول فایل
لازم است از داشتن دو توصیفگرفایل مستقل از یکدیگر که روی یک منبع یا مقصد کار کنند، پیشگیری نماییم. میتوانیم با دونسخهای نمودن توصیفگرفایل، این کار را انجام دهیم:
$ grep proud file 'not a file'> proud.log 2 >& 1
برای درک این مطالب لازم است توجه داشته باشید که: تغییر مسیر فایل از چپ به راست خوانده میشود.این ترتیبی است که BASH آنها را پردازش میکند. اول،
یک FD دونسخهای با دو FD مستقل از یکدیگر که به یک محل اشاره میکنند، متفاوت عمل مینماید. عملیات نوشتن در هر دونسخه دقیقاً همانند است. آشفتگی مربوط به یک FD اشاره کننده به ابتدای فایل در حالیکه دیگری قبلاً آن را ادامه داده وجود نخواهد داشت.
مراقب باشید ترتیب آنها را اشتباه نکنید:
$ grep proud file 'not a file'2 >& 1 > proud.log
این مثال
$ grep proud file 'not a file'&> proud.log
این نیز همان
در مستندات گنو: Duplicating File Descriptors, Moving File Descriptors, Opening File Descriptors for Reading and Writing
در پرسش و پاسخهای رایج:
چطور میتوانم چند دستور را در یک مرحله تغییر مسیر بدهم؟
چطور میتوانم خروجی 'time' را به یک متغیر یا فایل تغییر مسیر بدهم؟
چگونه میتوانم از dialog برای دریافت ورودی کاربر استفاده کنم؟
اساسی ترین شکل دستکاری ورودی و خروجی در BASH تغییر مسیر است. تغییر مسیر برای تغییر منبع داده یا مقصد توصیفگرهای فایل یک برنامه کاربردی به کار میرود. به طریقی، که میتوانیدخروجی برنامه را به جای ترمینال به یک فایل ارسال کنید، یا برنامهای داشته باشید که به جای خواندن از صفحه کلید از یک فایل بخواند.
همچنین، تغییر مسیر به اشکال مختلف میباشد. که عبارتند از تغییر مسیر فایل، دستکاری توصیفگرفایل، Heredocها و Herestringها.
در مستندات گنو: Redirections
تغییر مسیر: شیوه تغییر یک توصیفگرفایل معین برای خواندن وردیاش از جای دیگر یا ارسال خروجیاش به جایی دیگر میباشد
تغییر مسیر فایل تغییر یک توصیفگر فایل منفرد برای اشاره به یک فایل را شامل میگردد. بیایید با یک تغییر مسیر خروجی شروع کنیم:
$ echo "It was a dark and stormy night. Too dark to write."> story $ catstory It was a dark and stormy night. Too dark to write.
عملگر
در نتیجه، فرمان echo خروجیاش را به ترمینال ارسال نمیکند، به جای آن، تغییر مسیر
بایستی توجه گردد که این تغییر مسیر فقط برای دستور منفرد echo که برایش به کار برده شده، مؤثر است . دستورات دیگری که بعد اجرا میشوند، ارسال خروجی خود به محل
سپس ما از برنامه cat برای چاپ محتویات آن فایل استفاده کردیم. cat برنامه کاربردی است که محتویات تمام فایلهایی که به عنوان شناسه به آن دادهایم را میخواند. سپس هر فایل را یکی پس از دیگری به
هشدار:مثالهای بسیار زیادی از کد ها و خودآموزهای شل در اینترنت به شما میگویند، هر جا احتیاج به خواندن محتویات یک فایل دارید از cat استفاده کنید. این کار ضرورت ندارد! cat در الحاق چند فایل به یکدیگر خوب خدمت میکند، یا به عنوان یک ابزار سریع در اعلان شل برای دیدن آنچه داخل یک فایل است. شما نباید از cat در اسکریپتهای خود برای لولهکشی فایلها به دستورات، استفاده کنید. تقریباً همیشه راههای بهتری برای انجام این کار وجود دارد. لطفاً این هشدار را به خاطر بسپارید. استفاده بیمورد از cat صرفاً به ایجاد یک پردازش اضافی منجر خواهد شد، و غالباً سرعت خواندن ضعیفتری را نتیجه میدهد، زیرا cat نمیتواند مفهوم آنچه میخواند و هدفی که داده، برای آن خوانده میشود را تشخیص دهد.
موقعی که ما برنامه cat را بدون هرگونه شناسهای به کار میبریم، به طور آشکاری نمیداند که کدام فایل را بخواند. در این وضعیت، cat به جای خواندن از یک فایل، فقط از
$ cat
حتی اعلان فرمان را به شما باز نمیگرداند! چه اتفاقی رخ میدهد؟ cat هنوز در حال خواندن از
$ cattest? test?
حالا چرا test? را دو مرتبه نمایش میدهد؟ خوب، همانطور که شما تایپ میکنید، ترمینال شما تمام کاراکترهایی را که به
بیایید یک تغییر مسیر ورودی را برای پیوست یک فایل به
$ cat< story The story of William Tell. It was a cold december night. Too cold to write.
نتیجه این دقیقاً همانند نتیجه cat
عملگرهای تغییر مسیر میتوانند با تقدم یک عدد همراه گردند. آن عدد توصیفگرفایلی که تغییر میکند را مشخص مینماید.
اجازه دهید با چند مثال خلاصه کنیم:
command
command 1
command
command
عدد مربوط به FD(توصیفگرفایل)
$for homedir in /home/* >do rm "$ homedir /secret " >done 2> errors
در این مثال، روی هر دایرکتوری(یا فایل) در شاخه /home حلقه ایجاد میکنیم. سپس ما سعی در حذف فایل
ممکن است توجه نموده باشید که عملگرتغییر مسیر روی فرمان rm نمیباشد، بلکه روی
اجازه بدهید ببینیم نتیجه حلقه ما چه بوده:
$ caterrors rm: cannot remove `/home/axxo/secret': No such file or directory rm: cannot remove `/home/lhunath/secret': No such file or directory
دو پیغام خطا در فایل ثبت خطا. دو نفر که فایل
اگر یک اسکریپت مینویسید، و پیشبینی میکنید که یک دستور معین تحت شرایطی ممکن است ناموفق باشد، اما نمیخواهید کاربر اسکریپت، با خطاهایی که ممکن است دستور ایجاد کند پریشان گردد، میتوانید FD آن را ساکت کنید. ساکت کردن آن به آسانی یک تغییر مسیر فایل معمولی است. فقط تمام خروجی برای آن FD را به سیاهچاله سیستم ارسال میکنیم:
$for homedir in /home/* >do rm "$ homedir /secret " >done 2 > /dev/null
فایل /dev/null همیشه خالی است، مسئلهای نیست که در آن چه مینویسید یا از آن میخوانید. همینطور، موقعی که ما پیغام خطاها را در آن مینویسیم، آنها ناپدید میشوند. فایل /dev/null همچنان مانند قبل خالی میماند. چنین است زیرا یک فایل معمولی نمیباشد، یک دستگاه مجازی است. بعضیها /dev/null را bit bucketزیرنویس مترجم 1 مینامند.
یک مطلب نهایی هست که باید در باره تغییر مسیر فایل بدانید. جالب است که شما میتوانید یک فایل ثبت وقایع برای نگهداری پیغامهای خطایتان ایجاد کنید، اما همانطور که قبلاً اشاره کردم، BASH موقعی که به یک فایل تغییر مسیر داده میشود، محتویات موجود آن فایل را ازبین میبرد. در نتیجه، هر دفعه که ما آن حلقه را برای حذف کردن فایلهای secret اجرا کنیم، فایل ثبت وقایع ماقبل از اینکه دوباره با پیغامهای جدید پر شود،از سر کوتاه و خالی میشود. چه کار باید بکنیم، اگر میخواهیم رکوردی از هر پیغام خطای تولید شده در حلقه را حفظ کنیم؟ چه کار کنیم که با هر بار اجرای حلقهزیرنویس مترجم 2 فایل از سر کوتاه نشود؟ چاره کار با دوگانه کردن عملگر تغییر مسیر به دست میآید.
$for homedir in /home/* >do rm "$ homedir /secret" >done 2 >> errors
!Hooray
درضمن، فاصله بین عملگر تغییر مسیر و نام فایل، اختیاری است. بعضی افراد مینویسند
تکرار مفید:
وقتی که یک برنامه به فایل دادهای نیاز دارد، و برای خواندن داده از
موقعی که برنامه ای طراحی مینمایید که میتواند دادهها را از منابع مختلفی تغذیه کند، واقعاً اغلب بهترین راه آنست که برنامه شما از
در مستندات گنو: Redirecting Input, Redirecting Output, Appending Redirected Output, Redirecting Standard Output and Standard Error
توصیفگرهای فایل (به طور کوتاه: FDها) روشی برای ارجاع برنامهها به فایلها، یا منابع دیگری که همانند فایلها کارمیکنند( از قبیل لولهها،دستگاهها، سوکتها، یا ترمینالها ) میباشند. FDها نوع مشابه اشارهگرها به منابع داده، یا محلهایی که اطلاعات میتوانند نوشته شوند، هستند. موقعی که چیزی از آن FD, خوانده یا در آن نوشته میشود، داده در حال نوشته شدن در منبع FD یا خوانده شدن از آن میباشد.
به طور پیشفرض، هر فرایند جدیدی با سه FD آغاز میشود. به این توصیفگرهای فایل با نامهای ورودی استاندارد، خروجی استاندارد و خطای استاندارد رجوع میشود.در حالت کوتاه شده به ترتیب
اجازه بدهید کمی این تعاریف را محسوستر کنیم. در اینجا یک نمایش تجربی از چگونگی کار ورودی استاندارد و خروجی استاندارد میآوریم:
$ read-p "What is your name? "name ; echo "Good day,$ name. Would you like some tea?"What is your name? lhunath Good day, lhunath. Would you like some tea?
read دستوری است که اطلاعات را از
پس
$ rmsecrets rm: cannot remove `secrets': No such file or directory
بدون داشتن فایلی به نام
به خاطر داشته باشید موقعی که اسکریپتها را ایجاد میکنید،شما باید پیغام خطاهای سفارشی خود را به توصیفگر
echo "Uh oh. Something went really bad..">& 2
توصیفگر فایل: یک شاخص عددی ارجاع به یکی از فرآیندهای فایل باز است. هر دستوری حداقل سه توصیفگر اصلی دارد: FD شماره 0،