يشير هذا النص فقط إلى تطبيق GNU الخاص بـ awk المعروف باسم gawk Witch هو الأكثر استخدامًا ويأتي مع أي توزيع عصري Linux / UNIX .
[دليل مستخدم GNU AWK] [GNU-AWK] هو مرجعه ، للأمثلة التي استخدمتها في الحالات الحقيقية التي اتخذت بشكل رئيسي من إجابات [stackoverflow] [SO].
AWK هي لغة مماثلة لـ Perl ، فقط أكثر أناقة.
- أرنولد روبنز
AWK هي لغة برمجة مصممة للمعالجة النصية وتستخدم عادة كأداة لاستخراج البيانات وإعداد التقارير. إنها ميزة قياسية لمعظم أنظمة التشغيل التي تشبه UNIX.
awk ...تم اشتقاق اسمها من ألقاب مؤلفيها - ألفريد A Ho و Peter W Einberger و Brian K Ernighan.
awk ...يبحث عن الخطوط التي تحتوي على أنماط معينة في الملفات أو الإدخال القياسي.
يستخدم في الغالب لاستخراج البيانات والإبلاغ مثل تلخيص المعلومات من إخراج برامج الأداة المساعدة الأخرى.
بناء جملة C-like
مدفوعة البيانات: إنها تصف البيانات التي تريد العمل معها ثم الإجراء الذي يجب القيام به عند العثور عليها.
pattern { action }
pattern { action }إذا كان البرنامج قصيرًا :
awk ' program ' input-file1 input-file2ملاحظة: احذر من مشكلات shell اقتباس 1 .
cmd | awk ' program ' ملاحظة: يعيد pipe توجيه إخراج الأمر اليسرى ( cmd ) إلى إدخال الأمر awk 2 .
عندما يكون الرمز طويلًا ، يكون عادة أكثر ملاءمة لوضعه في ملف وتشغيله بأمر مثل هذا:
awk -f program-file input-file1 input-file2
cmd | awk -f program-file أو اجعلها قابلة للتنفيذ مثل shebang :
#! /bin/awk -f
BEGIN { print " hello world!! " } -F fs اضبط متغير FS على fs .
-v var=val قم بتعيين المتغير var على القيمة val قبل تنفيذ البرنامج.
ملاحظة : يمكن استخدامه أكثر من مرة ، وضع متغير آخر في كل مرة.
هذه الأنماط الخاصة أو كتل الإجراءات بدء التشغيل والتنظيف لبرامج awk .
BEGIN{
// initialize variables
}
{
/pattern/ { action }
}
END{
// cleanup
}BEGIN قبل قراءة سجل الإدخال الأول ، END بعد استهلاك جميع المدخلات.
$ echo " hello " | awk ' BEGIN{print "BEGIN";f=1}
{print $f}
END{print "END"} '
BEGIN
hello
ENDgrepping إذا كان لديك awk ؟؟ $ cat lorem_ipsum.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.$ grep dolor lorem_ipsum.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.$ awk ' /dolor/ ' lorem_ipsum.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.ملاحظة: إذا لم يتم منح الإجراء ، فإن الإجراء الافتراضي هو طباعة السجل الذي يطابق النمط المحدد.
ولكن ... كيف يمكننا معرفة الكلمة الأولى والأخيرة من كل سطر؟
بالطبع يمكن grep ، ولكن يحتاج إلى خطوتين:
$ grep -Eo ' ^[^ ]+ ' lorem_ipsum.dat
Lorem
Maecenas
Nunc
Curabitur
Lorem
Aliquam$ grep -Eo ' [^ ]+$ ' lorem_ipsum.dat
elit.
condimentum.
ex.
tellus.
elit.
ultrices. دعونا نرى awk في العمل هنا:
$ awk ' {print $1,$NF} ' lorem_ipsum.dat
Lorem elit.
Maecenas condimentum.
Nunc ex.
Curabitur tellus.
Lorem elit.
Aliquam ultrices. تقسم awk المدخلات لبرنامجك إلى سجلات وحقول .
يتم فصل السجلات بواسطة حرف يسمى فاصل السجل RS . بشكل افتراضي ، فاصل السجل هو حرف UNIX NewLine n .
هذا هو السبب في أن السجلات ، بشكل افتراضي ، خطوط مفردة .
بالإضافة إلى ذلك ، لدى awk فاصل سجل ORS للتحكم في طريقة تقديم السجلات إلى stdout .
يجب أن تكون RS و ORS محاطة بعلامات اقتباس ، والتي تشير إلى ثابت سلسلة.
لاستخدام حرف مختلف أو إعادة تعيينه ببساطة إلى متغيرات RS أو / ORS :
BEGIN قبل معالجة أي إدخال ، بحيث يتم قراءة السجل الأول مع الفاصل المناسب.أمثلة:
$ awk ' BEGIN{RS=" *, *";ORS="<<<---n"}
{print $0} ' lorem_ipsum.dat
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci <<< ---
euismod id nisi eget <<< ---
interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat <<< ---
et facilisis neque ultrices.
<<< --- $ awk ' {print $0} ' RS= " *, * " ORS= " <<<---n " lorem_ipsum.dat
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci <<< ---
euismod id nisi eget <<< ---
interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat <<< ---
et facilisis neque ultrices.
<<< --- يتم تحليل سجلات awk تلقائيًا أو فصلها إلى أجزاء تسمى الحقول .
بشكل افتراضي ، يتم فصل الحقول عن طريق المسافة البيضاء (أي سلسلة من المساحات أو أكثر من المساحات أو علامات التبويب أو الخطوط الجديدة) ، مثل الكلمات في خط.
للإشارة إلى حقل في برنامج awk ، يمكنك استخدام علامة دولار $ تليها عدد الحقل الذي تريده.
وبالتالي ، يشير $1 إلى الحقل الأول ، $2 إلى الثاني ، وهلم جرا.
مهم : $0 يمثل سجل الإدخال بالكامل.
$ awk ' {print $3} ' lorem_ipsum.dat
dolor
erat
orci,
dapibus
dolor
mauris NF هو متغير محدد مسبقًا هو عدد الحقول في السجل الحالي . لذلك ، سيكون $NF دائمًا الحقل الأخير من السجل.
$ awk ' {print NF} ' lorem_ipsum.dat
8
7
10
4
8
10مقابل
$ awk ' {print $NF} ' lorem_ipsum.dat
elit.
condimentum.
ex.
tellus.
elit.
facilisis. تحتفظ FS بقيمة فاصل الحقل ، هذه القيمة عبارة عن سلسلة أحادية الشحوم أو regex تتطابق مع الفواصل بين الحقول في سجل الإدخال.
القيمة الافتراضية هي " " ، وهي سلسلة تتكون من مساحة واحدة. كاستثناء خاص ، تعني هذه القيمة أن أي تسلسل من المساحات وعلامات التبويب و/أو الخطوط الجديدة هو فاصل واحد.
بنفس الطريقة التي لدينا ORS لدينا متغير OFS لإدارة كيفية إرسال حقولنا إلى دفق الإخراج.
$ cat /etc/group
nobody: * :-2:
nogroup: * :-1:
wheel: * :0:root
daemon: * :1:root
kmem: * :2:root
sys: * :3:root
tty: * :4:root$ awk ' !/^(_|#)/&&$1=$1 ' FS= " : " OFS= " <-> " /etc/group
nobody < - > * < - > - 2< - >
nogroup < - > * < - > - 1< - >
wheel < - > * < - > 0 < - > root
daemon < - > * < - > 1 < - > root
kmem < - > * < - > 2 < - > root
sys < - > * < - > 3 < - > root
tty < - > * < - > 4 < - > root ملاحظة : ummm ... $1=$1 ؟؟؟؟ 3
وضع السجلات والحقول في الاعتبار ، أصبحت الآن مستعدة لفهم الكود السابق لدينا:
$ awk ' {print $1,$NF} ' lorem_ipsum.dat
Lorem elit.
Maecenas condimentum.
Nunc ex.
Curabitur tellus.
Lorem elit.
Aliquam ultrices.هاتان متغيران مدمجان مفيدون:
NR : تم معالجة عدد سجلات الإدخال awk منذ بداية تنفيذ البرنامج.
FNR : رقم السجل الحالي في الملف الحالي ، إعادة تعيين awk FNR إلى الصفر في كل مرة يبدأ فيها ملف إدخال جديد.
$ cat n1.dat
one
two$ cat n2.dat
three
four$ awk ' {print NR,FNR,$0} ' n1.dat n2.dat
1 1 one
2 2 two
3 1 three
4 2 fourسلسلة التنسيق تشبه إلى حد كبير تلك الموجودة في ISO C.
بناء الجملة:
printf format, item1, item2, …
$ awk ' {printf "%20s <-> %sn",$1,$NF} ' lorem_ipsum.dat
Lorem < - > elit.
Maecenas < - > condimentum.
Nunc < - > ex.
Curabitur < - > tellus.
Lorem < - > elit.
Aliquam < - > ultrices. يتم توجيه الإخراج من print و printf إلى الإخراج القياسي بشكل افتراضي ولكن يمكننا استخدام إعادة التوجيه لتغيير الوجهة.
تتم كتابة التوجيه في awk تمامًا مثل إعادة التوجيه في أوامر Shell ، باستثناء أنها مكتوبة داخل برنامج awk .
$ awk ' BEGIN{print "hello">"hello.dat"} ' $ awk ' BEGIN{print "world!">>"hello.dat"} ' $ cat hello.dat
hello
world !من الممكن أيضًا إرسال الإخراج إلى برنامج آخر من خلال أنبوب :
$ awk ' BEGIN{sh="/bin/sh";print "date"|sh;close(sh)} '
dom nov 13 18:36:25 CET 2016 يمكن توجيه [الجداول] إلى stdin و stdout و stderr .
على سبيل المثال ، يمكننا كتابة رسالة خطأ إلى stderr مثل هذا:
$ awk ' BEGIN{print "Serious error detected!" > "/dev/stderr"} '
Serious error detected ! في AWK ، تكون المصفوفات مرتبطة ، كل واحد عبارة عن مجموعة من الأزواج أو الفهرس - القيمة ، حيث يمكن أن يكون أي رقم أو سلسلة فهرس.
لا يلزم إعلان ؛ يمكن إضافة أزواج جديدة في أي وقت.
| فِهرِس | قيمة |
|---|---|
| "بيرو" | "كلب" |
| "جاتو" | "قطة" |
| "UNO" | "واحد" |
| 1 | "واحد" |
| 2 | "اثنين" |
لإحالة صفيف:
array[index-expression]
لتعيين القيم:
array[index-expression] = value
للتحقق مما إذا تم فهرسة مفتاح:
indx in array
لتكرارها:
for (var in array) {
var, array[var]
}باستخدام القيم الرقمية كفهارس والحفاظ على الترتيب:
for (i = 1 ; i < = max_index ; i++) {
print array[i]
}مثال كامل:
$ cat dict.dat
uno one
dos two
tres three
cuatro fourawk ' {dict[$1]=$2}
END{if ("uno" in dict)
print "Yes we have uno in dict!"
if (!("cinco" in dict))
print "No , cinco is not in dict!"
for (esp in dict){
print esp, "->" ,dict[esp]
}
} ' dict.datيمنحك:
Yes we have uno in dict !
No , cinco is not in dict !
uno - > one
dos - > two
tres - > three
cuatro - > four لا يقوم gawk بفرز المصفوفات افتراضيًا:
awk ' BEGIN{
a[4]="four"
a[1]="one"
a[3]="three"
a[2]="two"
a[0]="zero"
exit
}
END{for (idx in a){
print idx, a[idx]
}
} ' 4 four
0 zero
1 one
2 two
3 threeولكن يمكنك الاستفادة من [procinfo] للفرز:
awk ' BEGIN{
PROCINFO["sorted_in"] = "@ind_num_asc"
a[4]="four"
a[1]="one"
a[3]="three"
a[2]="two"
a[0]="zero"
exit
}
END{for (idx in a){
print idx, a[idx]
}
} ' 0 zero
1 one
2 two
3 three
4 four gensub(regexp, replacement, how [, target]) : هي الوظيفة الأكثر تطوراً لاستبدال السلسلة.
وبدائل أبسط:
gsub(regexp, replacement [, target])
sub(regexp, replacement [, target])
وجود هذا الملف:
$ cat lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.سنقوم بتبديل موقع الكلمات الموضوعة على اليسار ويمين كل فاصلة.
$ awk ' {print gensub(/([^ ]+)( *, *)([^ ]+)/,
"\3\2\1", "g")} ' lorem.dat
Lorem ipsum dolor sit consectetur, amet adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim euismod, orci id nisi interdum, eget cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit consectetur, amet adipiscing elit.
Aliquam interdum mauris volutpat nisl et, placerat facilisis. باستخدام gensub نلتقط ثلاث مجموعات ثم نقوم بتبديل الطلب.
لتوضيح إجراء أبسط دعنا نغير النقاط من أجل الفواصل :
awk ' $0=gensub(/./, ",", "g") ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Maecenas pellentesque erat vel tortor consectetur condimentum,
Nunc enim orci, euismod id nisi eget, interdum cursus ex,
Curabitur a dapibus tellus,
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Aliquam interdum mauris volutpat nisl placerat, et facilisis, باستخدام gsub البديل:
awk ' gsub(/./, ",") ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Maecenas pellentesque erat vel tortor consectetur condimentum,
Nunc enim orci, euismod id nisi eget, interdum cursus ex,
Curabitur a dapibus tellus,
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Aliquam interdum mauris volutpat nisl placerat, et facilisis,يبدو هذا الخيار أفضل عندما لا تكون هناك حاجة إلى التقاط مجموعة .
وظائف أخرى مثيرة للاهتمام هي index و substr .
index(in, find)
substr(string, start [, length ])
مثل هذا:
$ awk ' BEGIN{t="hello-world";print index(t, "-")} '
6$ awk ' BEGIN{t="hello-world";print substr(t,index(t, "-")+1)} '
world يتم استخدام وظيفة split لإنشاء صفيف من سلسلة تقسيمها على شار فاصل ، فهي تُرجع عدد عناصر الصفيف الذي تم إنشاؤه.
split(string, array [, fieldsep [, seps ] ])
$ cat passwd
jd001:x:1032:666:Javier Diaz:/home/jd001:/bin/rbash
ag002:x:8050:668:Alejandro Gonzalez:/home/ag002:/bin/rbash
jp003:x:1000:666:Jose Perez:/home/jp003:/bin/bash
ms004:x:8051:668:Maria Saenz:/home/ms004:/bin/rbash
rc005:x:6550:668:Rosa Camacho:/home/rc005:/bin/rbash$ awk ' n=split($0, a, ":"){print n, a[n]} ' passwd
7 /bin/rbash
7 /bin/rbash
7 /bin/bash
7 /bin/rbash
7 /bin/rbashملاحظة : يمكن القيام بذلك بطريقة أكثر بساطة:
$ awk ' {print NF,$NF} ' FS= ' : ' passwd
7 /bin/rbash
7 /bin/rbash
7 /bin/bash
7 /bin/rbash
7 /bin/rbashاكتب وظيفة مخصصة بسيطة للغاية:
awk ' function test(m)
{
printf "This is a test func, parameter: %sn", m
}
BEGIN{test("param")} 'أعطنا:
This is a test func, parameter: param يمكننا أيضًا تقديم تعبير باستخدام عبارة return :
awk ' function test(m)
{
return sprintf("This is a test func, parameter: %s", m)
}
BEGIN{print test("param")} 'التحليل بالمعلمة هو الطريقة الوحيدة لاتخاذ متغير محلي داخل وظيفة.
يتم تمرير القيم العددية بالقيمة والمصفوفات بالرجوع إليها ، لذلك سوف ينعكس أي تغيير على صفيف داخل وظيفة في النطاق العالمي:
awk ' function test(m)
{
m[0] = "new"
}
BEGIN{m[0]=1
test(m)
exit
}
END{print m[0]} 'المخرجات:
newتحدياتنا :
01. كلمة ما قبل الأخيرة من السجل.
02. استبدال سجل.
03. ضع فاصلة فاصلة في نهاية كل سجل.
04. ضع فاصلة بين كل كلمة.
05. كل معا؟
06. إعادة توجيه سجلات غريبة إلى ملف وحتى تلك لآخر.
07. بالنظر إلى ملف كلمة المرور ، احصل على الحقل المفقود.
08. تبادل الحقل.
09. Traceroute القرصنة.
10. أين أطفالي؟
11. تجميع البيانات.
12. السجلات بين نمطين.
13. التحول الميداني.
14. السجلات للأعمدة.
15. معالجة ملف fasta.
16. الإبلاغ المعقد.
17. ملفات JOONER.
18. Passwd والمجموعة.
19. اتصالات المستخدم.
20.
وجود هذا الملف المصدر :
$ cat lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.$ awk ' {print $(NF-1)} ' lorem.dat
adipiscing
consectetur
cursus
dapibus
adipiscing
neque ليس أكثر من اللازم لشرح هنا ، تخزن NF عدد الحقول في السجل الحالي ، وبالتالي فإن NF-1 يشير إلى الحقل قبل الأخير و $(NF-1) سيكون قيمته.
مهمتنا ، استبدال سجل الملفات ، يجب أن يصبح السطر الثالث:
This not latin
لا شيء أكثر بساطة ، فقط العب حول NR ( عدد السجلات ).
شفرة:
$ awk ' NR==3{print "This is not latin";next}{print} ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
This is not latin
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis. الحل البديل لتجنب العبارة next : تعيين السطر الجديد إلى السجل الكامل $0 .
مثال:
$ awk ' NR==3{$0="This is not latin"}1 ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
This is not latin
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.$ awk ' 1 ' ORS= " ;n " lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit. ;
Maecenas pellentesque erat vel tortor consectetur condimentum. ;
Nunc enim orci, euismod id nisi eget, interdum cursus ex. ;
Curabitur a dapibus tellus. ;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. ;
Aliquam interdum mauris volutpat nisl placerat, et facilisis neque ultrices. ; نظرًا لأن RS الافتراضي هو خط Break Unix n نحتاج فقط إلى بادئة Semicolon إلى فاصل سجل الإخراج OFS .
1 ؟ 4
$ awk ' {$1=$1}1 ' OFS= ' , ' lorem.dat
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit.
Maecenas,pellentesque,erat,vel,tortor,consectetur,condimentum.
Nunc,enim,orci,,euismod,id,nisi,eget,,interdum,cursus,ex.
Curabitur,a,dapibus,tellus.
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit.
Aliquam,interdum,mauris,volutpat,nisl,placerat,,et,facilisis,neque,ultrices. الجزء الأكثر أهمية في هذا الرمز هو كيف يفرض إعادة بناء السجل بمبلغ $1=$1 للقيمة الحالية لـ OFS .
$ awk ' {$1=$1}1 ' OFS= ' , ' ORS= ' ;n ' lorem.dat
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit. ;
Maecenas,pellentesque,erat,vel,tortor,consectetur,condimentum. ;
Nunc,enim,orci,,euismod,id,nisi,eget,,interdum,cursus,ex. ;
Curabitur,a,dapibus,tellus. ;
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit. ;
Aliquam,interdum,mauris,volutpat,nisl,placerat,,et,facilisis,neque,ultrices. ; ببساطة مثل اللعب مع إخراج اسكواش: OFS و ORS .
لنبدأ بالحل النهائي:
$ awk ' NR%2{print > "even.dat";next}
{print > "odd.dat"} ' lorem.dat$ cat even.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Lorem ipsum dolor sit amet, consectetur adipiscing elit$ cat odd.dat
Maecenas pellentesque erat vel tortor consectetur condimentum.
Curabitur a dapibus tellus.
Aliquam interdum mauris volutpat nisl placerat, et facilisis. تجد وظيفة [modulo] ( % ) الباقي بعد التقسيم للحصول على رقم قياسي الحالي NR مقسومًا على اثنين:
$ awk ' {print NR%2} ' lorem.dat
1
0
1
0
1
0 بقدر ما نحن الآن حتى الآن ، في awk 1 صحيح و 0 خطأ . نعيد توجيه ناتجنا لتقييم هذه الحقيقة.
next ، يتطلب اهتمامًا خاصًا ، فهو يجبر awk على إيقاف عملية السجل الحالية على الفور والانتقال إلى واحد التالي.
وبهذه الطريقة ، نتجول في حالة مزدوجة ستبدو هكذا:
awk ' NR % 2{print > "even.dat"}
!NR % 2{print > "odd.dat"} ' lorem.dat$ cat /etc/passwd
jd001:x:1032:666:Javier Diaz::/bin/rbash
ag002:x:8050:668:Alejandro Gonzalez::/bin/rbash
jp003:x:1000:666:Jose Perez::/bin/bash
ms004:x:8051:668:Maria Saenz::/bin/rbash
rc005:x:6550:668:Rosa Camacho::/bin/rbash دعنا نفترض الدليل المنزلي عن طريق بادئة السلسلة الثابتة "/home/" إلى اسم المستخدم :
$ awk ' $6="/home/"$1 ' FS= ' : ' OFS= ' : ' /etc/passwd
jd001:x:1032:666:Javier Diaz:/home/jd001:/bin/rbash
ag002:x:8050:668:Alejandro Gonzalez:/home/ag002:/bin/rbash
jp003:x:1000:666:Jose Perez:/home/jp003:/bin/bash
ms004:x:8051:668:Maria Saenz:/home/ms004:/bin/rbash
rc005:x:6550:668:Rosa Camacho:/home/rc005:/bin/rbashيجب أن تكون خطوتنا الأولى هي النظر في فاصل المجال ، القولون ، للمدخلات وكذلك للإخراج.
ثم نحتاج إلى العثور على موضع حقل الفراغ ، 6 لهذا المثال.
أخيرًا ، نقوم بتكوين القيمة المطلوبة باستخدام السلسلة المحددة وتخزين تسجيل الدخول المستخدم في الحقل الأول.
print لأن قيمة إرجاع التخصيص $6 ستكون دائمًا صحيحة والإجراء الافتراضي awk هو طباعة السجل المتأثر.
هدفنا: يجب أن يصبح المجال الأخير الأول والأول.
الرمز النهائي:
$ awk -F : ' {last=$1;$1=$NF;$NF=last}1 ' FS= " : " OFS= ' : ' /etc/passwd
/bin/rbash:x:1032:666:Javier Diaz:/home/jd001:jd001
/bin/rbash:x:8050:668:Alejandro Gonzalez:/home/ag002:ag002
/bin/bash:x:1000:666:Jose Perez:/home/jp003:jp003
/bin/rbash:x:8051:668:Maria Saenz:/home/ms004:ms004
/bin/rbash:x:6550:668:Rosa Camacho:/home/rc005:rc005 نحن نلعب مع متغير وسيط يستخدم لتخزين قيمة الحقل الأولى ، ونعود قيمته مع آخرها ، وأخيراً نقوم بتعيين متغير last إلى $NF ( $NF=last ).
وجود هذا المخرج:
$ traceroute -q 1 google.com 2> /dev/null
1 hitronhub.home (192.168.1.1) 5.578 ms
2 217.217.0.1.dyn.user.ono.com (217.217.0.1) 9.732 ms
3 10.127.54.181 (10.127.54.181) 10.198 ms
4 62.42.228.62.static.user.ono.com (62.42.228.62) 35.519 ms
5 72.14.235.20 (72.14.235.20) 26.003 ms
6 216.239.50.133 (216.239.50.133) 25.678 ms
7 mad01s24-in-f14.1e100.net (216.58.211.238) 25.019 msنحن بحاجة إلى حساب الحزمة إجمالي وقت السفر .
$ traceroute -q 1 google.com 2> /dev/null |
awk ' {total+=$(NF-1)}
END{print "Total ms: "total} '
Total ms: 153.424لأنه لا يتم تحديد أي شرط ، يتم تنفيذ الإجراء لجميع السجلات .
total+=$(NF-1) : يتم استخدام total المتغير لتجميع قيمة كل حقل قبل الأخير $(NF-1) .
أخيرًا ، نستخدم قاعدة END لإظهار القيمة total النهائية.
مهمتنا: الحصول على عملياتنا المعتمدة على shell .
$ echo $$
51026أول شيء : إطلاق عمليات الخلفية.
$ sleep 10 & sleep 15 & sleep 20 &
[1] 86751
[2] 86752
[3] 86753 باستخدام أداة ps ، سوف تبحث awk عن الحقل الثالث المعروف باسم PPID .
ملاحظة : نحن نستخدم -v لتعيين PPID VAR قبل تنفيذ البرنامج.
$ ps -ef | awk -v ppid= $$ ' $3==ppid '
501 86751 51026 0 7:57PM ttys001 0:00.00 sleep 10
501 86752 51026 0 7:57PM ttys001 0:00.00 sleep 15
501 86753 51026 0 7:57PM ttys001 0:00.00 sleep 20
0 86754 51026 0 7:57PM ttys001 0:00.00 ps -ef
501 86755 51026 0 7:57PM ttys001 0:00.00 awk $3 ==51026نحتاج فقط إلى النوم :
$ ps -ef | awk -v ppid= $$ ' $3 == ppid && /slee[p]/
{print $2" -> "$5} '
86751 - > 7:57PM
86752 - > 7:57PM
86753 - > 7:57PM يحتاج الحل إلى حالة جديدة لإضافتها: العثور على نمط النوم في سجلنا الحالي /slee[p]/ .
سيكون الإجراء الذي تم تشغيله هو طباعة الحقل الثاني $2 مع حاملات PID و $5 ، وهو الطابع الزمني .
وجود هذا الملف:
$ cat ips.dat
IP BYTES
81.220.49.127 328
81.220.49.127 328
81.220.49.127 329
81.220.49.127 367
81.220.49.127 5302
81.226.10.238 328
81.227.128.93 84700 مهمتنا هي حساب عدد البايتات لكل IP .
$ awk ' NR>1{ips[$1]+=$2}
END{for (ip in ips){print ip, ips[ip]}} ' ips.dat
81.220.49.127 6654
81.227.128.93 84700
81.226.10.238 328حفنة من الأشياء هنا لشرح.
NR>1{ips[$1]+=$2} : يتم تنفيذ ips[$1]+=$2 عندما يكون رقم السجل الحالي أكبر من NR>1 . هذا ضروري لتجنب الرأس.
ips عبارة عن صفيف مفهرس بواسطة قيمة IP (حقل $1 ) ، لكل مفتاح سنتراكم في قيمة الحقل الثاني.
لاحظ حقيقة مهمة ، إذا لم يكن المفتاح موجودًا في الصفيف ، awk يضيف عنصرًا جديدًا إلى الهيكل ، وإلا فإنه سيقوم بتحديث القيمة السابقة التي أشار إليها هذا المفتاح (كما في مثالنا).
يتم استخدام قاعدة END لتكرار الصفيف بواسطة الفهارس والقيم.
يمكن إعادة كتابة هذا الرمز بطريقة مختلفة تمامًا لتجنب استخدام المصفوفات والحفاظ على الترتيب الذي تم الاستفادة منه من ملف IPS الذي تم فرزه:
awk ' NR==1{next}
lip && lip != $1{print lip,sum;sum=0}
{sum+=$2;lip=$1}
END{print lip,sum} ' ips.dat
81.220.49.127 6654
81.226.10.238 328
81.227.128.93 84700 NR==1{next} : تجاوز الرأس.
lip && lip != $1{print lip,sum;sum=0} : هنا نستخدم VAR lip ( Last-IP ). lip && lip != $1 عندما لا تكون lip فارغة وقيمة لا تساوي الحقل الأول (الذي يحمل عنوان IP الحالي) ، سيكون الإجراء الذي تم تشغيله هو طباعة lip sum إجمالي كمية البايت للبايتات الأخيرة. ثم نهيئه sum=0 .
الحيلة واضحة ، في كل مرة يغير IP ( $1 ) ، نعرض إحصائيات الإحصائيات السابقة.
{sum+=$2;lip=$1} : لتحديث sum+=$2 وتعيين IP الحالي إلى lip : lip=$1 . هذه هي الخطوة الأخيرة من معالجة السجلات لدينا.
يتم استخدام الكتلة END لطباعة القيم المعلقة .
يحافظ هذا الرمز على الترتيب ، ولكن في رأيي ، يأتي هذا على حساب تعقيد زيادة كبيرة.
مهمتنا هي استخراج الخطوط بين OUTPUT END .
$ cat pat.dat
test -3
test -2
test -1
OUTPUT
top 2
bottom 1
left 0
right 0
page 66
END
test 1
test 2
test 3 هذا مثال كلاسيكي يستخدم لتوضيح كيفية عمل مطابقة الأنماط في awk وأفعاله المساعد التي كرستها [Post].
$ awk ' /END/{flag=0}flag;/OUTPUT/{flag=1} ' pat.dat
top 2
bottom 1
left 0
right 0
page 66 استنادًا إلى قيمة متغير flag ، سيكون صحيحًا ( 1 ) عند العثور على OUTPUT نمط البدء وخطأ ( 0 ) عند الوصول إلى علامة END .
لتجنب خطوة إضافية ، يكون ترتيب الإجراء مهمًا للغاية ، إذا اتبعنا تسلسل المنطق :
$ awk ' /OUTPUT/{flag=1}flag;/END/{flag=0} ' pat.dat
OUTPUT
top 2
bottom 1
left 0
right 0
page 66
ENDتظهر علامات النمط من خلال الإخراج.
السبب: بعد العثور على نمط OUTPUT ، يتم تنشيط العلامة ، حيث أن الإجراء التالي يعتمد على هذا العلامة ، يتم طباعة السجل.
يمكننا تجنب هذا السلوك وضع تنشيط العلم كخطوة الأخيرة من التدفق.
لنفترض هذا الملف:
$ cat space.dat
10.80 kb
60.08 kb
35.40 kb
2.20 MB
1.10 MB
40.80 kb
3.15 MB
20.50 kbتتمثل مهمتنا في حساب سجلاتنا الإجمالية في بايت ميجا :
$ awk ' {total+= $1 / ($2=="kb" ? 1024: 1)}
END{print total} ' space.dat
6.61365لفهم كيفية عمله ، يجب أن يكون أحد المفاهيم واضحًا ، المشغل [الثلاثية] (موضوع منشور قديم).
سيتم استخدام total لتجميع Divison of the First Pibility $1 بمقدار $2 ثاني سيحتفظ بالقيمة التي قدمها المشغل الثلاثية : 1024 عندما يكون $2 يساوي kb و 1 إذا لم يكن هناك حاجة إلى تحويل.
أخيرًا ، نطبع القيمة total في الكتلة END .
المصدر الأصلي:
$ cat group.dat
string1
string2
string3
string4
string5
string6
string8مهمتنا هي تجميع سجلات في كتل من ثلاثة أعمدة مثل هذا:
string1 string2 string3
string4 string5 string6
string8 قد يبدو الأمر معقدًا ، لكنه يصبح أكثر بساطة إذا فهمنا كيفية استخدام فاصل حقل الإخراج OFS :
$ awk ' ORS = NR%3 ? FS : RS; END{print "n"} ' group.dat
string1 string2 string3
string4 string5 string6
string8 إذا قمنا بتعيين ORS على حرف فارغ ، والقيمة الافتراضية FS ، فسيصبح كل الإخراج سطرًا واحدًا:
$ awk ' ORS=FS; END{print "n"} ' group.dat
string1 string2 string3 string4 string5 string6 string7 ORS = NR%3 ? FS : RS : أخيرًا نستخدم المشغل الثلاثي (الذي تم شرحه قبل ذلك) لتقييم النتيجة [Modulo] NR%3 لتقسيم رقم الحقل الحالي NR بمقدار ثلاثة.
إذا كان الباقي صحيحًا ، فإن ORS تصبح FS ، وهي مساحة فارغة ، وإلا فسيتم تعيين القيمة الافتراضية RS ، فاحصل على خط UNIX n .
في المعلوماتية الحيوية ، [fasta] هو تنسيق ملف مستند إلى النص.
وجود المثال التالي:
$ cat fasta.dat
> header1
CGCTCTCTCCATCTCTCTACCCTCTCCCTCTCTCTCGGATAGCTAGCTCTTCTTCCTCCT
TCCTCCGTTTGGATCAGACGAGAGGGTATGTAGTGGTGCACCACGAGTTGGTGAAGC
> header2
GGT
> header3
TTATGATنحتاج إلى الطول الكلي لكل تسلسل ، واستئناف نهائي .
يجب أن تبدو هكذا:
> header1
117
> header2
3
> header3
7
3 sequences, total length 127 awk هي الأداة المثالية لجهد الإبلاغ هذا ، لهذا المثال سوف نستخدم:
awk ' /^>/ { if (seqlen) {
print seqlen
}
print
seqtotal+=seqlen
seqlen=0
seq+=1
next
}
{
seqlen += length($0)
}
END{print seqlen
print seq" sequences, total length " seqtotal+seqlen
} ' fasta.dat
يرتبط الإجراء الأول باكتشاف الرأس /^>/ ، هذا لأن جميع الرؤوس نجوم ذات حرف > .
عندما لا تكون seqlen لاغية قيمتها ، يتم طباعة طول التسلسل السابق ، على stdout المرفقة بالرأس الجديد. يتم تحديث seqtotal وتهيئة seqlen لخدمة التسلسل التالي. أخيرًا ، نقوم باكتساب مزيد من المعالجة السجل مع next .
يتم استخدام الإجراء الثاني {seqlen += length($0)} لتحديث seqlen لتلخيص إجمالي طول السجل.
الغرض من القاعدة END هو إظهار التسلسل غير المطبوع والإجماليات.
الخدعة هنا هي طباعة طول التسلسل السابق عندما وجدنا رأسًا جديدًا .
عندما نقوم بمعالجة السجل الأول ، ليس لدى seqlen أي قيمة ، لذا فإننا نتخطى التصور.
مصدر:
$ cat report.dat
snaps1: Counter: 4966
Opens: Counter: 357283
Instance: s.1.aps.userDatabase.mount275668.attributes
snaps1: Counter: 0
Opens: Counter: 357283
Instance: s.1.aps.userDatabase.test.attributes
snaps1: Counter: 5660
Opens: Counter: 37283
Instance: s.1.aps.userDatabase.mount275000.attributes واجبنا : قم بإنشاء تقرير لتصور snaps instance ولكن فقط عندما تكون علامة العداد الأولى في SNAP أكبر من الصفر .
الإخراج المتوقع:
snaps1: Counter: 4966
Instance: s.1.aps.userDatabase.mount275668.attributes
snaps1: Counter: 5660
Instance: s.1.aps.userDatabase.mount275000.attributesنحن نلعب مرة أخرى حول الأنماط والأعلام :
awk ' {$1=$1}
/snaps1/ && $NF>0{print;f=1}
f && /Instance/ {print;f=0} ' report.dat لكل سجل يتم تنفيذ الإجراء الأول ، يجبر awk إعادة بناء السجل بأكمله ، باستخدام القيم الحالية لـ OFS 3 .
تتيح لنا هذه الخدعة تحويل فاصل فضاء متعدد إلى char واحد ، القيمة الافتراضية لفاصل حقل الإخراج.
لنرى هذا:
$ awk ' 1 ' text.dat
one two
three four$ awk ' $1=$1 ' text.dat
one two
three four يتم تشغيل الإجراء الثاني عندما يتم العثور على النمط والحقل الأخير أكبر من الصفر /snaps1/ && $NF>0 .
awk يطبع السجل وتعيين قيمة حقيقية print;f=1 .
الخطوة الأخيرة: عندما يكون العلم صحيحًا ونمط المثيل في السطر f && /Instance/ ، أظهر الخط وإلغاء تنشيط العلم: print;f=0 .
لنفترض اثنين من المحفوظات:
$ cat join1.dat
3.5 22
5. 23
4.2 42
4.5 44$ cat join2.dat
3.5
3.7
5.
6.5 نحتاج إلى السجلات من One One join1.dat عندما تكون الحقول الأولى في الثانية join2.dat .
يجب أن يكون الإخراج:
3.5 22
5. 23يمكننا استخدام Unix Join Utility ، بالطبع ، لكننا نحتاج إلى فرز الملف الأول:
$ join <( sort join1.dat ) join2.dat
3.5 22
5. 23 غير مطلوب في awk :
$ awk ' NR == FNR{a[$1];next}
$1 in a ' join2.dat join1.datدعونا ندرس المرشحات والإجراءات:
NR == FNR : رقم السجل يساوي رقم ملف السجل يعني أننا نقوم بمعالجة الملف الأول الذي تم تحليله إلى awk : join2.dat .
الإجراءات الزوجية a[$1];next سيكون إضافة قيمة void جديدة إلى الصفيف المفهرس بواسطة الحقل الأول. سيقوم البيان next بتقسيم معالجة السجلات وينتقل التدفق إلى آخر .
بالنسبة للإجراء الثاني ، يتم تطبيق NR != FNR ضمنيًا ويؤثر فقط على join1.dat ، والشرط الثاني هو $1 in a سيكون صحيحًا عندما يكون الحقل الأول من join1.dat مفتاح صفيف.
هذه هي UNIX الكلاسيكية :
$ cat /etc/group
dba:x:001:
netadmin:x:002:$ cat /etc/passwd
jd001:x:1032:001:Javier Diaz:/home/jd001:/bin/rbash
ag002:x:8050:002:Alejandro Gonzalez:/home/ag002:/bin/rbash
jp003:x:1000:001:Jose Perez:/home/jp003:/bin/bash
ms004:x:8051:002:Maria Saenz:/home/ms004:/bin/rbash
rc005:x:6550:002:Rosa Camacho:/home/rc005:/bin/rbashهدفنا ، تقرير مثل هذا:
d001:dba
ag002:netadmin
jp003:dba
ms004:netadmin
rc005:netadminنحتاج إلى تدفق ملفات متعددة كما درسنا في مثالنا الأخير:
$ awk -F : ' NR == FNR{g[$3]=$1;next}
$4 in g{print $1""FS""g[$4]} ' /etc/group /etc/passwd للمعالجة /etc/group نكرر مقارنة NR == FNR ثم قم بتخزين اسم المجموعة $1 التي تم فهرسها بواسطة معرفها $3 : g[$3]=$1 . أخيرًا ، نقوم باكتساب مزيد من المعالجة السجل مع next .
ستستهدف الشرط الثاني سجلات /etc/passwd فقط ، عندما يكون الحقل الرابع $4 ( معرف المجموعة ) موجودًا في المصفوفة $4 in g ، سنقوم بطباعة تسجيل الدخول والقيمة التي أشار إليها الصفيف المفهرس بواسطة معرف المجموعة g[$4] ، لذلك: print $1""FS""g[$4] .
مثال إخراج المستخدمين:
$ users
negan rick bart klashxx klashxx ironman ironman ironmanسنحسب عمليات تسجيل الدخول لكل مستخدم.
$ users | awk ' {a[$1]++}
END{for (i in a){print i,a[i]}} ' RS= ' + '
rick 1
bart 1
ironman 3
negan 1
klashxx 2يتم تنفيذ الإجراء لجميع السجلات.
a[$1]++ : هذا هو العداد ، لكل مستخدم $1 ، يزيد القيمة المدببة (VARS غير المكتسب لها القيمة الرقمية صفر).
في END ، قم بتكرار الصفيف بواسطة المفتاح والقيمة المخزنة لتقديم النتائج.
إخراج نموذجي :
$ uptime
11:08:51 up 121 days, 13:09, 10 users, load average: 9.12, 14.85, 20.84كيف يمكننا الحصول على متوسط الحمل المتوسط ؟
$ uptime | awk ' {printf "Load average mean: %0.2fn",
($(NF-2)+$(NF-1)+$(NF))/3 } ' FS= ' (:|,) + '
Load average mean: 14.94هذه تقنية جديدة.
نحن نستخدم [regex] كفاصل المجال (:|,) + ، بحيث يمكن أن يكون FS قولونًا وفاصلة تليها صفر أو مسافات فارغة.
نحتاج فقط إلى الحقول الثلاثة الأخيرة لأداء الحساب المطلوب ، ثم نستخدم printf متصلة بقناع مناسب .
إذا كنت لا تزال هنا ، شكرا !!
من وجهة نظري ، awk هي لغة استخفاف وتحتاج إلى الكثير من الحب ❤.
إذا كنت جائعًا للمزيد ، فأخبرني بذلك في قسم التعليقات ، وسأفكر في الجزء الثاني لإنهاء مهمتي ... تحملك حتى الموت.
ترميز سعيد!
[دليل إلى Unix Shell اقتباس] [دليل نقلا عن]. ↩
[ويكيبيديا على خطوط الأنابيب] [الأنابيب]. ↩
هناك أوقات يكون فيها من المناسب إجبار awk على إعادة بناء السجل بأكمله ، باستخدام القيم الحالية لـ FS و OFS .
للقيام بذلك ، نستخدم المهمة غير الضارة على ما يبدو: $1 = $1 ↩ ↩ 2
إجابة سريعة ، إنه مجرد اختصار لتجنب استخدام بيان الطباعة.
في awk عندما يتم مطابقة الشرط ، فإن الإجراء الافتراضي هو طباعة خط الإدخال.
$ echo "test" |awk '1'
يعادل:
echo "test"|awk '1==1'
echo "test"|awk '{if (1==1){print}}'
ذلك لأن 1 سيكون دائمًا [صحيح]. ↩