با سلام این روزا که از ساعت ۴ تا ۱۲ شب نت ها همه داخلی میشن به خاطر جلوگیری از اعتراضات توسط جمهوری اسلامی تصمیم گرفتم چیزایی که یاد میگیرم رو توی سایت بنویسم.

حتما شده که بخواید فایلی رو بدون اینکه بازش کنید ویرایش کنید منظورم اینه که گاهی شما یک شل Interactive دارید که میتونید توش ابزار های vim یا nano رو استفاده کنید ولی گاهی ممکنه یه شل ساده داشته باشید که نتونید توش دستوراتی نیاز به یک شل اینتراکتیو دارن رو باز کنید و pipe کردن هم جواب نمیده در این صورت این آموزش خیلی به کارتون میاد و در یک جای دیگه هم کاربرد داره. اونجایی که شما دسترسی نوشتن روی یک فایل رو ندارید ولی دسترسی خوندن رو دارید و میخواید که فایل رو بخونید و تغییر بدید و همونجا جایگزین کنید و owner اون فایل خودتون بشید (که بتونید بنویسید) برای اینکار یه دستور خیلی ساده داره که محشره.

این دستور میتونه هم برای دیتایی که درون فایل هست استفاده بشه و هم میتونه از stdin بخونه یعنی از پایپ استفاده کنید.

برای استفاده از این دستور باید با فلگ هاش آشنا بشید اولین فلش و مهمترین فلگش -e هست که اگر هم نذارید اهمیتی نداره ولی باید مقدارشو بنویسید حتما.

echo "salam man mohammad hastam" | sed 's/mohammad/ali/' 
RESULT: salam man ali hastam

همانطور که میبینید اسم محمد رو با اسم علی جایگزین کرد حالا فلگ -e دقیقا باید پشت همون کوتیشن قرار میگرفت که الزامی هم نیست.

حالا شما میتونید بهش فایل هم بدید که ادرس فایل باید در انتها داده بشه برای مثال

sed -e '12 s/true/false/' ./index.php

توی دستور بالا شماره خط ۱۲ داده شده که به این معناست که اولین true توی خط ۱۲ رو به فالس تغییر بده. اگر توی یک رنج خط میخواید اینکارو بکنید باید اینجوری بنویسید:

sed '12,20 s/true/false/' ./index.php

اینجوری تمامی true ها توی خطوط ۱۲ تا ۲۰ به false تغییر میکنه توجه کنید اگر دوتا true در یک خط باشه فقط اولین ها توی هرخط تغییر میکنید اگه میخواید دومی ها تغییر کنن باید اینجوری بنویسید.

sed 's/true/false/g' ./index.php

و اگر لازم باشه که تمامی true ها در هر خط (از بین خطوط مشخص شده) تغییر کنه باید اون ۲ رو به g تغییر بدید تا تمامی true ها توی خط تبدیل به false بشه.

توجه کنید که با این کارها فایل دست نخورده میمونه و فقط تغییرات توی کنسول شما چاپ میشه برای اینکه بتونید تغییرات رو ذخیره کنید روی همون فایل باید از فلگ -i استفاده کنید که کارش اینه که اصل فایل رو (بکاپ) رو به یه اسم دیگه ای ذخیره میکنه و فایل اصلی رو تغییر میده (حتی اگر که شما owner اون نباشید اونو پاک میکنه و فایلی با owner شما ایجاد میکنه و تغییرات رو ذخیره میکنه پس خیلی حواستون باشه) اگر که نیاز به بکاپ ندارید ولی میخواید سیو بشه فلگ -i رو خالی بدید اینجوری

sed -i '' -e 's/true/false/g' ./index.php

اینجوری میتونید فایل رو ذخیره کنید ولی خودشون خیلی تاکید میکنن که اینکارو نکنید چون ممکنه اشتباهی یه فایل رو خراب کنید و دیگه نتونید کاری کنید بخاطر همین حتما فایل بکاپ رو درست کنید و برای اینکار همونجایی که خالی هست یه اسم بنویسید تا اون اسم ته اسم فایل اصلی نوشته بشه. توجه کنید با اینکار فایلی که شما با هریوزری ویرایش میکنید جایگزین فایل اصلی میشه (یعنی فایل اصلی پاک میشه و یه فایل با همون پرمیشن ها ولی با ownerبودن شما ایجاد میشه) ولی فایل بکاپ با همون owner میمونه و میتونید بعد از اینکه کارتون رو کردید و دوست داشتید که برگردونید فایلی که ویرایش کردید و خودتون owner هستید رو پاک کنید و اون فایل اصلی رو rename کنید تا همه چیز بدون تغییر بمونه.

حتی میتونید فلگ -e رو هم خالی بذارید تا فقط کاربردی که فایل رو کپی کنه و فایل اصلی رو هم تغییر نام بده شما رو هم owner فایل جدید کنه بکنید.

کدوم خط رو میخواید ویرایش کنید؟

همونطور که در بالا تر گفتم میتونید خط رو همون اول مشخص کنید اگر که میخواید از یک رنج انتخاب کنید مثلا از خط ۱ تا ۵ باید از همون چیزی که در بالاتر گفتیم یعنی 1,5 استفاده کنید و اگر هم میخواید از خط ۵ تا ۳ خط بعدش رو انتخاب کنید باید اینجوری بنویسید

sed '5,+3 s/true/false/' ./index.php

اینجوری از خط ۵ تا ۸ رو میگیره و اگر هم خواستید به آخرین خط فایل چیزی اضافه کنید با $ میتونید آخرین خط رو بگیرید اینجوری

sed '$ s/true/false/' ./index.php

حالا فرض کنید که شماره خط ندارید و میخواید کلا با یک ریجکس خط هایی که با فلان کلمه شروع میشه یا خط هایی که شامل فلان عبارت میشن رو تغییر بدید باید اینجوری بنویسید:

sed '/^this is/ s/mohammad/ali/' ./index.php

در مثال بالا خط هایی که با this is شروع شدن رو انتخاب میکنه و توی اون خط ها اون جایگزینی mohammad با ali رو انجام میده.

نکته مهم: توی همون s/mohhamad/ali هم میتونید به جای mohammad یه رجکس بنویسید.

 از این ابزار میتونید برای خوندن خطی که میخواید هم استفاده کنید و راه درستش اینه که از فلگ -n استفاده کنید (این فلگ کارش اینه که خطی که شما بهش میدید رو نشون میده) نمیشه از این فلگ همزمان با فلگ -e استفاده کرد و این فلگ برای خوندن خط مشخصی از فایل استفاده میشه

sed -n '2p' index.php
sed -n '2,+10p' index.php
sed -n '2,10p;9q' index.php

دستور اول خط دوم رو چاپ میکنه p مخفف پرینت هست

خط دوم هم از خط دوم تا ۲+۱۰ یعنی ۱۲ رو چاپ میکنه 

توی خط سوم گفتیم بعد از اینکه ۹ خط رو که گفتیم چاپ کردی دیگه ادامه نده (بیشتر برای فایل های بزرگ از این استفاده میشه که لازم نباشه تا انتها بخونه فایل رو)

حالا میشه این کار رو با دستور زیر هم انجام داد ولی اینکار در واقع فیلتر کردن متن هست.

sed '7!d' index.php

این دستور همه خط ها به جز خط ۷ رو دلیت میکنه (d) که باعث میشه فقط خط ۷ بمونه و اون چاپ میشه (این کار عملا خوندن نیست و جزء همون تغییرفایل با فلگ -e هست)

اگر رنج خط رو خواستید مشابه بالا میتونید ویرگول بذارید و مثل بالا یا عدد بدید یا از + استفاده کنید.

حالا فرض کنید که میخواید یه خط به زیر خط ۵ اضافه کنید باید به این شکل اقدام کنید:

sed '5 a salam' index.php

با اینکار خط ۵ سرجاش میمونه و این دستور روی خط ۶ کلمه سلام رو مینویسه اگر که میخواید خط ۵ اینتر بخوره بره پایین و کلمه سلام روی خط ۵ نوشته بشه باید از تابع i به جای a استفاده کنید (insert).

اون ۵ اول هم میتونه مثل همیشه ریجکس باشه ولی باید حتما درون دوتا / باشه که متوجه بشه رجکسه.

آپدیت:

از اونجایی که شیوه تغییر محتوای فایل در sed با استفاده از فلگ -i به این صورته که ابتدا فایل temporary ایجاد میکنه و بعد فایل اصلی رو حذف و فایل تمپ رو تغییر نام میده پس owner فایل عوض میشه و اینکه اگر watcherی وجود داشته باشه که فایل رو watch کنه با حذف فایل broke میشه. در این مواقع که میخوایم ویرایش فایل به صورت درجا با بدون تغییر در owner فایل باشه باید از دستور ed استفاده کنیم که مشابه همین sed هست ولی کمی کار باهاش متفاوته.

اولین و مهمترین تفاوتش اینجاست که توی sed ما باااااید فلگ -e رو ست میکردیم یا مقدارشو میدادیم (یعنی مقدار بدون نوشتن اسم فلگ) ولی اینجا توی ed ما کافیه اسم فایل رو بنویسیم و بعدش دونه دونه دستورات (توابع مثل replace) رو اجرا کنیم و نهایتا فایل رو ذخیره کنیم و خارج بشیم. اما این چیزی نیست که به درد یک شل غیر اینتراکتیو بخوره پس برای انجام اینکار با یه شل ساده در یک خط باید از پایپ استفاده کنیم. توجه کنید که این دستور بعد از اجرا واقعا فایل رو بدون گرفتن بکاپ ذخیره میکنه پس خیلی حواستون باشه چیز اشتباهی انجام ندید.

echo -e "s/true/false/g\nwq" | ed index.php

با دستور بالا عملا ما تابع replace رو اجرا میکنیم و بعدش در خط بعدی (\n) دستور wq که جداگانه به معنای write (یعنی ذخیره کردن توی فایل) و q به معنای خروج هست.

دستور بالا در واقع اجرا میشه و مثل sed عمل میکنه با این تفاوت که فایل رو حذف نمیکنه و همچنین owner فایل رو تغییر نمیده  (پس اگر شما قابلیت نوشتن متن توی اون فایل رو نداشته باشید نمیتونید تغییری ایجاد کنید برخلاف sed)

موفق باشید