Skip to content

Latest commit

 

History

History
218 lines (137 loc) · 12.8 KB

sed-tutorial.md

File metadata and controls

218 lines (137 loc) · 12.8 KB

Sed

معرفی sed

sed یک ویرایشگر ویژه برای ویرایش خودکار فایل است. اگر شما قصد دارید اسکریپتی بنویسید تا فایلی را ویرایش کنید، sed ابزاری است که برای این منظور می توانید از آن استفاده کنید.

یک حقیقت تلخ

sed یک ویرایشگر جریان (Stream editor) است. اگر با این مفهوم ارتباط برقرار نمی کنید، کاری که با لوله کشی (Piping) می کنید را تصور کنید.

دستورات اصلی: s برای جاگذاری (substitution)

sed دستورات زیادی دارد ولی اکثرا تنها دستور s را می شناسند و استفاده می کنند. s تمامی بخش هایی که با regex مورد نظر همخوانی دارد را با مقدار جدید جایگزین می کند. در مثا زیر کلمه day در فایل old با کلمه night جایگزین می شود و نتیجه در فایل new ذخیره می شود:

sed s/day/night/ <old >new
یا روش دیگر:
sed s/day/night/ old >new
یامی توانید این روش را امتحان کنید:
echo day | sed s/day/night

در مثال بالا خروجی night خواهد بود.

من در این مثال از quote استفاده نکردم، به دلیل آنکه نیاز نداشت. اگر راهنماهای دیگر راخوانده باشید می دانید که به چه دلیل نیاز ندارد ولی من به شماتوصیه می کنم که از quote استفاده کنید. اگر در دستورات از meta-characters ها استفاده کنید به quote نیاز پیدا خواهید کرد. در صورتی که مطمئن نیستید حتما از quote استفاده کنید و بهتر است که به آن عادت کنید. من در ادامه در تمامی مثال ها برای تاکید بر اهمیت استفاده از quote از آن استفاده خواهم کرد. برای نمونه مثال قبلی :

sed 's/day/night/' <old >new

من باید تاکید کنم که sed عینا مقداری را که شما به آن اشاره می کنید تغییر می دهد. دستور زیر را اجرا کنید:

echo Sunday | sed 's/day/night/' 

مقدار خروجی Sunnight خواهد بود . چرا که تنها مقدار day که شما به آن اشاره داشتید جایگزین می شود.

این دستور جایگزینی یا substitution دارای چهار بخش است:

  s	  Substitute command

  /../../	  Delimiter )جدا کننده)

  day	  Regular Expression Pattern Search Pattern

  night	  Replacement string

در آینده quoting و regular expression را نیز در مقاله های مجزا پوشش خواهم داد. این دو ۹۰ درصد کار در دستورات جایگزینی است.

اسلش به عنوان جداکننده

کاراکتری که بعد از s استفاده می شود یک جداکننده یا delimiter است. برای این منظور مرسوم است که از / استفاده شود به دلیل آنکه در ed، more و vi از آن استفاده می شود. در صورتی که شما بخواهید یک مسیر در فایل سیستم را جایگزین کنید، برای مثال /usr/local/bin/ را با common/bin/ باید کاراکترهای / را با escape کنید.\

sed 's/\/usr\/local\/bin/\/common\/bin/' <old >new

بسیاری به این حالت «حصار چوبی» یا "Picket fence" می گویند و خوانا نیست. در صورتی که بجای / از _ برای جداکننده استفاده کنید بسیار خواناتر خواهد بود:

sed 's_/usr/local/bin_/common/bin_' <old >new

بعضی ها از colons:

sed 's:/usr/local/bin:/common/bin:' <old >new

و بعضی از | استفاده می کنند:

sed 's|/usr/local/bin|/common/bin|' <old >new

شما می توانید هر کدام را که دوست دارید انتخاب کنید. هر کدام که کمتر در رشته شما استفاده شده. توجه داشته باشید که شما به سه جدا کننده نیاز دارید. در صورتی که با پیغام "Unterminated `s' command" مواجه شدید، بدانید که یکی از جداکننده ها را فراموش کرده اید.

استفاده از & به عنوان رشته مورد جستجو

برخی مواقع شما می خواهید یک رشته را جستجو کنید و به آن کاراکتری اضافه کنید، مانند پرانتر یا اضافه کردن یک کلمه به کلمه قبلی. این کار در صورتی که یک کلمه مشخص را جستجو می کنید بسیار ساده است:

sed 's/abc/(abc)/' <old >new

این روش زمانی که شما خروجی ثابتی برای الگوی (pattern) مورد نظر ندارید کار نخواهد کرد. برای این منظور از کاراکتر ویژه "&" استفاده می کنیم. این کاراکتر دقیقا برابر با خروجی مورد جستحو است:

sed 's/[a-z]*/(&)/' <old >new

شما هر چند بار که بخواهید می توانید از کاراکتر & استفاده کنید:

$ echo "123 abc" | sed 's/[0-9]*/& &/'
123 123 abc

بگذارید کمی بیشتر در مورد این مثال توضیح بدهم. اولین تطابق برای [0-9]* اولین کاراکتر این خط است. به این معنی که هر عددی از صفر تا نه. بنابراین اگر ورودی "abc 123" باشد، خروجی تغییری نمی کند. راه درست اینکه تنها اعداد تکرار شود این است که الگو تنها اعداد را شامل شود:

$ echo "123 abc" | sed 's/[0-9][0-9]*/& &/'
123 123 abc

رشته abc بدون تغییر باقی خواهد ماند. زیرا که با regex مورد نظر منطبق نیست. در صورتی که می خواهید abc از رشته شما حذف شود باید regex خود را توسعه دهید به شکلی که با بخش های دیگر نیز منطبق شود و به طور مشخص بخش های مورد نظر خود را به کمک "(", ")" و "\1" که در قسمت های بعدی توضیح داده خواهند شد.

استفاده از 1\ برای نگهداشتن بخشی از متن

"(" ")" و "1" از مباحث مربوط به regular expression هستند. شما با استفاده از آنها می توانید بخش هایی از regex را مستثنی کنید. "1" بخش اول الگو است و "2" بخش دوم الگو است. sed تا ۹ بخش را می تواند نگه دارد.

درصورتی که شما می خواهید کلمه اول یک خط را نگه دارید و بقیه را پاک کنید می توانید بخش مورد نظر را با پرانتز جدا کنید:

sed 's/\([a-z]*\).*/\1/'

برای نوشتن چنین دستوری باید بیشتر دقت کرد. regex ها بزرگترین حالت ممکن را شامل می شوند. در اینجا "[a-z]*" از صفر تا هر تعداد حروف کوچک را شامل می شود. *. از صفر تاهر تعدا کاراکتر را شامل می شود. اولین الگو تمام حروف کوچک و الگوی دوم هر آنچه بعد از الگوی اول باشد را شامل می شود. به همین دلیل در صورتی که شما دستور زیر را صادر کنید:

echo abcd123 | sed 's/\([a-z]*\).*/\1/'

خروجی abcd خواهد بود و تمامی اعداد حذف می شوند.

در صورتی که بخواهید جای دو کلمه را عوض کنید می توانید دو الگو را حفظ کنید و جای آن دو را عوض کنید:

sed 's/\([a-z]*\) \([a-z]*\)/\2 \1/'

به فاصله بین دو الگو بازیابی شده (\2 \1) دقت کنید. این برای این است که مشخص باشد که دو کلمه یافت شده است. در حالتی که تنها یک الگو منطبق شده است، در خروجی تاثیری نخواهد داشت. در صورتی که تاکید دارید که حتما هر کلمه یک حرف را شامل شود:

sed 's/\([a-z][a-z]*\) \([a-z][a-z]*\)/\2 \1/'

"1" حتما نباید در قسمت جاگذاری یا همان سمت راست باشد. شما می توانید از آن در در بخش الگو نیز از آن استفاده کنید. در صورتی که شما بخواهید یک کلمه که تکرار شده را حذف کنید می توانید مانند زیر عمل کنید:

sed 's/\([a-z]*\) \1/\1/'

شما می توانید تا "9" را استفاده کنید.

پرچم های جاگذاری Substitution flags

شما می توانید پرچم های بیشتری را بعد از جداکننده آخر یا همان سوم استفاده کنید. شما می توانید از طریق آنها مشخص کنید که درصورتی که الگوی مورد نظر بیش از یک بار در یک خط یافت شد با آن چه رفتاری داشته باشد.

g/ جاگذاری سراسری (Global replacement)

اغلب ابزار های یونیکسی که بر روی فایل کار می کنند، یک خط را در یک مرتبه بررسی می کنند. sed نیز از همین روال پیروی می کند. به این معنی که تنها اولین تطابق در یک خط را در نظر می گیرد، در صورتی که شما می خواهید کل خط را بررسی کند و تمامی موارد یافت شده را اصلاح نماید. برای مثال در نظر بگیرید که می خواهید کلیه کلمات در یک خط را به طور جداگانه داخل یک پرانتز قرار دهید. در این مثال به جای استفاده از "[A-Za-z]" که لغاتی مانند ",won't" را شامل نمی شود از ",[^]" که با هر چیز به غیر از فاصله منطبق می شود استفاده می کنیم. در عین حال این الگو با هر چیزی منطبق می شود ، چرا که * به معنی صفر یا هر تعداد است.

sed 's/[^ ]*/(&)/' <old >new

در صورتی که می خواهید تمامی لغات در خط را شامل شود g را به آخر آن اضافه کنید:

sed 's/[^ ][^ ]*/(&)/g' <old >new

آیا sed بازگشتی (Recurcive) است؟

sed الگوها را تنها بر روی داده های ورودی اعمال می کند. این به این معنی است که یک خط را می خواند، زمانی که به مورد منطبق با الگو رسید خروجی را تولید می کند و سپس از ادامه خط شروع به جستجو می کند. به همین دلیل شما نباید نگران دستوراتی مانند زیر باشید:

sed 's/loop/loop the loop/g' <old >new

دستور بالا باعث ایجاد یک حلقه بی پایان نمی شود. اگر دستور s دومی ایجاد شود خروجی قبلی ویرایش خواهد شد. در بخش های آتی نحوه اجاری چندین دستور توضیح داده خواهد شد.

1/,2/ و ... انتخاب مورد تطابق

بدون مشخص کردن هیچ پرچمی اولین مورد تطابق با الگو تغییر خواهد کرد. با قرار داده پرچم g تمامی تطابق ها تغییر خواهند کرد. در صورتی که بخواهید تنها مورد دوم تغییر کند می توانید از "(" و ")" به همراه "1" برای آنکه مورد اول تغییر نکند استفاده کنید. دراین مثال اولین مورد بدون تغییر باقی می ماند و مورد دوم پاک می شود:

sed 's/\([a-zA-Z]*\) \([a-zA-Z]*\) /\1 /' <old >new

راه ساده تری هم برای این کار موجود است. شما می توانید با قرار دادن یک عدد بعد از دستور مشخص کنید که تغییرات بر روی چندمین تطابق باید انجام شود:

sed 's/[a-zA-Z]* //2' <old >new

شما می توانید پرچم g را به همراه عدد استفاده کنید. برای مثال برای اینکه مورد اول را رها کنید و مورد های دیگر یعنی ۲ و ۳ و... را تغییر دهید می توانید از 2g/ استفاده کنید:

sed 's/[a-zA-Z]* /DELETED /2g' <old >new

دقت کنید "2/" را با "2" اشتباه نگیرید.

منابع