پرسش و پاسخ شماره ۷۹ - آموزش اسکریپت نویسی
X
تبلیغات
رایتل

آموزش اسکریپت نویسی

آموزش اسکریپت نویسی پوسته گنو-لینوکس

#!/bin/bash

پرسش و پاسخ شماره ۷۹

پرسش و پاسخ شماره ۷۹

چطور می‌توانم grep را برای یافتن سطرهای شامل foo و bar همچنین foo یا bar به کار ببرم؟ یا برای فایل‌های شامل foo و bar، احتمالاً در سطرهای جداگانه؟

این در واقع سه پرسش مختلف است، بنابراین ما پاسخ را به سه قسمت تفکیک کرده‌ایم.

foo و bar در یک سطر

آسانترین روش برای مطابقت با سطرهایی که شامل هر دو رشته foo و bar هستند استفاده از دو فرمان grep می‌باشد:

grep foo | grep bar
grep foo "$myfile" | grep bar   # برای آنهایی که نیاز به نگهداری دستی دارند

این کار همچنین می‌تواند با یک egrep انجام بشود، هر چند(به طوریکه احتمالاً می‌توانید حدس بزنید) این فرمان نمی‌تواند به طور واقعی برای سنجش بیش از دو الگو به کار برود:

egrep 'foo.*bar|bar.*foo'

اگر ترجیح می‌دهید، می‌توانید با یک جمله sed یا awk به این مقصود نائل شوید:

sed -n '/foo/{/bar/p}'
awk '/foo/ && /bar/'

اگر نیاز به راه حل سنجش awk برای تعداد اختیاری الگوها دارید، می‌توانید یک سطر فرمان awk ممتد طرح‌ریزی نمایید:

# bashو ksh93 درپوسته‌های‎
# را ایجاد می‌کند ‎awk "/$1/&&/$2/&&...."‎
# داده‌هایی که می‌خواهند مطابقت شوند باید در ورودی استاندارد باشند‎
# سطرهای انطباق یافته را در خروجی استاندارد می‌نویسد‎
multimatch() {
  (($# < 2)) && { echo "usage: multimatch pat1 pat2 [...]" >&2; return 1; }
  awk "/$1/$(printf "&&/%s/" "${@:2}")"
}

یا، نگارش POSIX:

# POSIX
multimatch() {
  [ $# -lt 2 ] && { echo "usage: multimatch pat1 pat2 [...]" >&2; return 1; }
  __p1=$1
  shift
  awk "/$__p1/$(printf "&&/%s/" "$@")"
}

افسوس، توابع POSIX متغیرهای محلی ندارند. همچنین، در صورتیکه هر یک از الگوها شامل کاراکترهای / باشند، هر دو مورد ناموفق می‌شوند. (اصلاح آن به عنوان یک تمرین برای خواننده واگذار گردیده است.)

یک نگارش POSIX که عبارتهای منظم را در اسکریپت awk تعبیه نمی‌کند.

# POSIX
multimatch() { 
  awk 'BEGIN{for(i=1;i<ARGC;i++) a[i]=ARGV[i]; ARGC=1} {for (i in a) if ($0 !~ a[i]) next; print}' "$@"
}

foo یا bar در یک سطر

روشهای بسیار زیادی برای مطابقت سطرهای شامل foo یا bar وجود دارد. با گزینه ‎ -e‎ می‌توان الگوهای چندتایی را با grep به کار برد:

grep -e 'foo' -e 'bar'

یا می‌توانید یک الگو با egrep (یا ‎grep -E‎) بسازید:

egrep 'foo|bar'
grep -E 'foo|bar'

(نمی‌توانید ازعملگر اتصال | با grep ساده استفاده کنید. این عملگر | فقط در عبارتهای منظم توسعه‌یافته معتبر می‌باشد.)

این کار همچنین می‌تواند با sed، awk، و غیره انجام بشود.

awk '/foo|bar/'

رویکرد awk برتری آن را دارد که به شما اجازه استفاده از سایر ویژگی‌های awk روی سطرهای انطباق یافته را می‌دهد.

مطابقت سطرهایی که شامل هیچ یک از رشته‌های foo و bar نیستند:

grep -E -v 'foo|bar'
# را ترجیح می‌دهند ‎egrep -v 'foo|bar'‎ بعضی اشخاص 

foo و bar در یک فایل، بدون لزوم حضور هر دو در یک سطر

اگر می‌خواهید مطابقت کنید که فایلها (به جای سطرها) شامل هر دو رشته foo و bar هستند، چندین رویکرد عملی وجود دارد. ساده‌ترین(اگر چه ضرورتاً کارآمدترین نیست) دوبار خواندن فایل است:

grep -q foo "$myfile" && grep -q bar "$myfile" && echo "Found both"

راه حل استفاده دوگانه ‎grep -q‎ این مزیت را دارد که در هر نوبت خواندن، در جایی که مورد انطباقی پیدا شود توقف می‌کند، بنابراین اگر یک فایل حجیم داشته باشید، اما کلمات در ابتدای آن باشند، فقط قسمت اول فایل خوانده می‌شود. متأسفانه، اگر انطباق‌ها در انتهای فایل باشند(وضعیت وخیم: در انتهایی‌ترین سطرهای فایل) شما کل فایل را دوبار می‌خوانید.

رویکرد دیگری یکبار خواندن فایل، و حفظ نمودن اثر آنچه دیده‌اید، در ضمن پیشروی در فایل می‌باشد. در awk:

awk '/foo/{a=1} /bar/{b=1} a&&b{print "both found";exit} END{if (a&&b){ exit 0} else{exit 1}}'

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

اگر می‌خواهید بررسی‌های اضافی در محتویات فایل انجام بدهید، این راهکار awk می‌تواند کاملاً به آسانی وفق داده شود.


پرسش و پاسخ 79 (آخرین ویرایش ‎2011-04-15 12:16:35‎ توسط GreyCat)