قد تشعر كمبرمج بالضياع عند استكشافك لأول مرة شيفرة مصدرية تحتوي على الآلاف من الأسطر، في هذه الحالة سيفيدك استخدام الأداة grep كثيرًا لحل تلك المشكلة، لذا سنستعرض في هذا المقال عدة طرق مفيدة لاستخدام الأمر grep
على لينكس مع أمثلة عليها.
أمثلة واقعية ومفيدة للأمر grep في لينكس
يصف الدليل man
الأداة grep
على أنها أداة لطباعة الأسطر المُطابقة لنمط ما، قد ينتقص ذلك الوصف من الإمكانيات والاستخدامات العديدة للأمر grep
، والتي تعد من أفضل الأدوات ضمن حزمة أدوات نظام يونكس، حيث ستجد نفسك بحاجة إليها في الكثير من الحالات عند تعاملك مع الملفات النصية.
تعد الأمثلة الواقعية أفضل مُعلم، سنوضح الاستخدامات المتعددة للأداة grep
بتطبيق أمثلتنا في هذا المقال على الشيفرة المصدرية للمكتبة Asciidoctor.js، يمكن تنزيل الشيفرة المصدرية للمكتبة من Github واختيار نفس الإصدار الذي تم تطبيق الأمثلة في هذا المقال عليه، وذلك للحصول على نتائج مطابقة للأمثلة المذكورة خلال تطبيق الأمثلة، يمكنك تنفيذ الأوامر التالية لتحميل الشيفرة المصدرية محليًا وتحديد الإصدار المطلوب العمل ضمنه:
git clone https://github.com/asciidoctor/asciidoctor.js cd asciidoctor.js git checkout v1.5.6-rc.1
1. الاستخدام الأساسي بالبحث عن نص
توفر Asciidoctor.js دعمًا لمحرك جافاسكريبت Nashorn لمنصة جافا، يمكننا البحث عن أماكن ذكر ذلك المحرك ضمن الشيفرة المصدرية للتعرف ولاستكشاف طريقة استخدام المكتبة له، يمكننا مثلًا البحث ضمن ملف توصيف الاعتماديات package.json عن كل ذكر لاسم المحرك Nashorn كالتالي:
$ grep nashorn package.json "test": "node npm/test/builder.js && node npm/test/unsupported-features.js && node npm/test/jasmine-browser.js && node npm/test/jasmine-browser-min.js && node npm/test/jasmine-node.js && node npm/test/jasmine-webpack.js && npm run test:karmaBrowserify && npm run test:karmaRequirejs && node npm/test/nashorn.js",
كما نلاحظ يوجد عدة اختبارات متعلقة بـ Nashorn، في المثال التالي سنحاول البحث أكثر عن استخدامات ذلك المحرك.
2. بحث غير حساس لحالة الأحرف ضمن مجموعة من الملفات
يمكننا البحث ضمن ملفات الاختبار التي وجدنا ذكر لها في المثال السابق، التي تقع ضمن المجلد ./npm/test/ والتي تحوي على ذكر مباشر لكلمة Nashorn، في هذه الحالة البحث الغير حساس لحالة الأحرف أفضل، بحيث ستظهر لنا جميع حالات ذكر اسم المكتبة مثل "nashorn" و"Nashorn" أو أي شكل آخر للكلمة مؤلف من حروف كبيرة وصغيرة، وذلك باستخدام الخيار -i
كالتالي:
$ grep -i nashorn npm/test/*.js npm/test/nashorn.js:const nashornModule = require('../module/nashorn'); npm/test/nashorn.js:log.task('Nashorn'); npm/test/nashorn.js:nashornModule.nashornRun('jdk1.8.0');
نلاحظ من النتائج التي ظهرت فائدة البحث الغير حساس لحالة الأحرف في تلك الحالات، حيث لم يظهر في المثال الأول ضمن النتائج العبارة التالية require('../module/nashorn')
، سنعاين هذا الملف بالتفصيل لاحقًا.
3. البحث عن الملفات الغير مطابقة
سنحاول معرفة ما إذا كان يوجد ملفات لا تتعلق بمحرك Nashorn ضمن المجلد npm/test/، يمكننا عكس نتيجة البحث والاستفادة من خيار طباعة الملفات الغير مطابقة لشرط البحث -L
كالتالي:
sh$ grep -iL nashorn npm/test/* npm/test/builder.js npm/test/jasmine-browser-min.js npm/test/jasmine-browser.js npm/test/jasmine-node.js npm/test/jasmine-webpack.js npm/test/unsupported-features.js
نلاحظ كيف أظهر لنا الأمر grep
باستخدام الخيار -L
ضمن الخرج أسماء تلك الملفات فقط، لا تحوي كل تلك الملفات على أي ذكر للنص "nashorn" ضمنها (بغض النظر عن حالة الأحرف للكلمة)، لا يعني ذلك أن تلك الملفات لا تتعلق بالمحرك nashorn، بل هي فقط لا تحوي على أحرف تلك الكلمة متتالية معًا.
4. البحث عن الأنماط ضمن الملفات المخفية والمجلدات الفرعية تكراريًا
استخدمنا في آخر مثالين نمط glob لتحديد الملفات التي نريد من الأمر grep
البحث ضمنها، لكن استخدام رمز النجمة ("*") يمنعنا من البحث ضمن الملفات المخفية، وكذلك ضمن الملفات الموجودة داخل المجلدات الفرعية، يمكن حل تلك المشكلة بدمج الأمر grep
مع أمر البحث عن الملفات find بدلًا من الاعتماد على تمرير نمط glob الخاص بالصَدفة بإحدى الطريقتين:
# الطريقة التالية غير فعالة، ستولد إجرائية جديدة لكل ملف $ find npm/test/ -type f -exec grep -iL nashorn \{} \; # الطريقة التالية تسبب مشاكل للملفات التي يحوي اسمها على فراغ $ grep -iL nashorn $(find npm/test/ -type f)
لكل من الطريقتين السابقتين مساوؤها كما هو مذكور ضمن التعليقات فوق الأوامر، فلحل مشكلة أسماء الملفات التي تحتوي على فراغ يمكنك البحث أكثر عن كيفية استخدام الخيار grep -z
مع الخيار -print0
للأمر find
،لكن الحل الأمثل يكون باستخدام خيار البحث التكراري-r
للأمر grep
، وعندها يمكننا تحديد المجلد الجذر الذي نريد البحث ضمنه فقط، بدلًا من تمرير قائمة بأسماء الملفات المراد البحث فيها، سيبحث الأمر grep
ضمن كل الملفات في المجلد المحدد، بما فيها المجلدات المخفية، ومن ثم سينزل تكراريًا إلى داخل المجلدات الفرعية ويعيد العملية:
$ grep -irL nashorn npm/test/npm/ npm/test/builder.js npm/test/jasmine-browser-min.js npm/test/jasmine-browser.js npm/test/jasmine-node.js npm/test/jasmine-webpack.js npm/test/unsupported-features.js
يمكن باستخدام هذا الخيار البحث أيضًا ضمن المجلد الأب npm لمعرفة الملفات المتعلقة بالمحرك Nashorn المغايرة لملفات الاختبار السابقة كالتالي:
$ grep -irL nashorn npm/
يمكنك اختبار الأمر السابق بنفسك ومشاهدة النتيجة، بحيث سيظهر لك نتائج أكثر.
5. تصفية الملفات بأسمائها باستخدام التعابير النمطية
كما لاحظنا سابقًا وجود ملفات اختبار متعلقة بالمحرك Nashorn ضمن ملفات المشروع، وبما أن محرك Nashorn مكتوب في لغة جافا، فيمكننا استكشاف ملفات جافا المصدرية ضمن المشروع التي تحوي ذكرًا صريحًا لاسم المحرك، وهناك طريقتين لتنفيذ ذلك بحسب إصدار grep
المستخدَم، الطريقة الأولى باستخدام grep
للعثور على الملفات التي تحوي النمط "nashorn" ثم تمرير نتيجة الأمر الأول إلى أمر grep
آخر يُصفي ملفات جافا فقط كالتالي:
$ grep -ir nashorn ./ | grep "^[^:]*\.java" ./spec/nashorn/AsciidoctorConvertWithNashorn.java:public class AsciidoctorConvertWithNashorn { ./spec/nashorn/AsciidoctorConvertWithNashorn.java: ScriptEngine engine = engineManager.getEngineByName("nashorn"); ./spec/nashorn/AsciidoctorConvertWithNashorn.java: engine.eval(new FileReader("./spec/nashorn/asciidoctor-convert.js")); ./spec/nashorn/BasicJavascriptWithNashorn.java:public class BasicJavascriptWithNashorn { ./spec/nashorn/BasicJavascriptWithNashorn.java: ScriptEngine engine = engineManager.getEngineByName("nashorn"); ./spec/nashorn/BasicJavascriptWithNashorn.java: engine.eval(new FileReader("./spec/nashorn/basic.js"));
النصف الأول من الأمر تم شرحه مسبقًا، سنشرح القسم الثاني من الأمر وهو "^[\^:]*\.java"، حيث يعتبر الأمر grep
افتراضيًا نمط البحث المٌمرر له تعبيرًا نمطيًا، إلا إذا استخدمنا الخيار -F
، مما يعني إضافةً لذكر نص صريح لمطابقته حرفيًا، يمكن استخدام محارف وصفية خاصة بالتعابير النمطية للتعبير عن أنماط بحث أعقد، فالنمط المستخدم في مثالنا تُعبر أقسامه عن التالي:
-
^
بداية سطر -
[^:]*
متبوعة بسلسلة من المحارف لا تحوي على النقطتين (":"). -
\.
متبوعة بنقطة، ولأن النقطة تملك معنى مميز في التعابير النمطية يجب حمايتها باستخدام خط مائل عكسي، مما يعني مطابقة النقطة حرفيًا. -
java
متبوعة بكلمة java.
بما أن grep
يفصل بين اسم الملف وسياق نتيجة البحث بمحرف النقطتين (":")، يمكننا الاستفادة من ذلك وإبقاء أسماء الملفات التي تنتهي بالامتداد.java
فقط في قسم اسم الملف، ويجب الانتباه إلى أن ذلك النمط سيُطابق أيضًا الملفات ذات الامتداد .javascript
، يمكنك محاولة حل تلك المشكلة بنفسك إن أردت.
6. تصفية الملفات بأسمائها باستخدام grep
التعابير النمطية قوية جدًا، لكن مثالنا بسيط ولا يحتاج لكل هذا التعقيد، في المثال السابق بحثنا في جميع الملفات عن النمط "nashorn" وتجاهلنا العديد من النتائج بعد تمريرها للقسم الثاني من الأمر، يوجد حل لتلك المشكلة في إصدار جنو GNU من الأمر grep
الذي يكون مثبتًا عادةً ضمن أنظمة لينكس، وهو باستخدام الخيار --include
الذي يوجّه الأمر grep
للبحث ضمن الملفات التي تتطابق أسمائها مع نمط glob الممرر فقط:
$ grep -ir nashorn ./ --include='*.java' ./spec/nashorn/AsciidoctorConvertWithNashorn.java:public class AsciidoctorConvertWithNashorn { ./spec/nashorn/AsciidoctorConvertWithNashorn.java: ScriptEngine engine = engineManager.getEngineByName("nashorn"); ./spec/nashorn/AsciidoctorConvertWithNashorn.java: engine.eval(new FileReader("./spec/nashorn/asciidoctor-convert.js")); ./spec/nashorn/BasicJavascriptWithNashorn.java:public class BasicJavascriptWithNashorn { ./spec/nashorn/BasicJavascriptWithNashorn.java: ScriptEngine engine = engineManager.getEngineByName("nashorn"); ./spec/nashorn/BasicJavascriptWithNashorn.java: engine.eval(new FileReader("./spec/nashorn/basic.js"));
7. البحث عن كلمات
استُخدم في مشروع Asciidoctor.js عدة لغات برمجة أساسها لغة روبي، ولاستخدامه في جافاسكريبت يجب أن يُصرّف باستخدام Opal، وهو مُصرّف مصدر إلى مصدر من روبي إلى جافاسكريبت، يمكننا محاولة فهم الواجهة البرمجية API الخاصة بـ Opal، عبر البحث عن كل ذكر للكائن العام Opal
ضمن ملفات جافاسكريبت في المشروع، قد يورد ذكر الكائن ضمن عبارة إسناد (Opal =
) أو ضمن عبارة وصول للخواص الأعضاء ضمن الكائن (Opal.
) أو ضمن سياق آخر، حيث يمكن استخدام التعابير النمطية للبحث عن ذلك، لكن الأمر grep
يوفّر حلولًا أخف من ذلك لمثل تلك المشكلات الشائعة، فباستخدام الخيار -w
سيتم مطابقة الكلمات فقط التي تُعرّف تقنيًا بأنها أنماط مسبوقة ومتبوعة بغير محارف الكلمات، تلك المحارف تكون إما محرف بداية السطر أو نهاية السطر أو أي محرف آخر ليس بحرف أو رقم أو شرطة سفلية:
$ grep -irw --include='*.js' Opal . ...
8. تلوين الخرج
لم ننسخ خرج الأمر السابق لوجود الكثير من النتائج، لكن يمكن تحسين الخرج الكثيف مثل هذا بتلوينه لتسهيل فهمه، في حال لم تكن هذه الميزة مُفعلة افتراضيًا على نظام التشغيل، يمكننا تفعيلها يدويًا لخرج الأمر الحالي بتمرير خيار جنو --color
كالتالي:
$ grep -irw --color=auto --include='*.js' Opal . ...
سيظهر نفس خرج الأمر السابق، لكن مع تلوين عبارة البحث ضمن النتيجة.
9. استخراج عدد الأسطر أو الملفات ضمن البحث
يمكننا معرفة طول نتيجة الأمر السابق الذي لم نعرضه كالتالي:
$ grep -irw --include='*.js' Opal . | wc -l 86
ما يعني أن لدينا 86 سطرًا يُطابق عبارة البحث ضمن كل الملفات التي جرى البحث ضمنها، يمكننا أيضًا معرفة عدد تلك الملفات التي استخرجت منها النتائج، يمكن باستخدام الخيار -l
تحديد خرج الأمر grep
بحيث يعرض لنا أسماء الملفات التي احتوت على نتيجة فقط بدلًا من أسطر النتائج، يمكننا بذلك معرفة عدد تلك الملفات كالتالي:
$ grep -irwl --include='*.js' Opal . | wc -l 20
قد يذكرنا هذا الخيار بالخيار -L
بحرف كبير، حيث تدل عادة الأحرف الكبيرة والصغيرة للخيارات على خيارات متضادة، فيُظهر لنا الخيار -l
أسماء الملفات المطابقة للبحث، بينما يظهر الخيار -L
الملفات الغير مطابقة للبحث، لمثال آخر على ذلك يمكنك الرجوع إلى الدليل والتحقق من الفرق بين الخيارين -h
و -H
، وبالعودة لنتيجة بحثنا حصلنا على نتيجتين هما مطابقة 86 سطرًا، و 20 مِلفًا، يمكننا أيضًا معرفة طبيعة توزع تلك الأسطر ضمن تلك الملفات، فباستخدام الخيار -c
للأمر grep
سيتم عد الأسطر المُطابقة ضمن كل ملف على حدى، بما فيها الملفات التي لا تحوي على نتائج:
$ grep -irwc --include='*.js' Opal . ...
يحتاج خرج الأمر السابق لمعالجة لاستخراج المعلومات المطلوبة، لأن ترتيب النتائج ضمنه تكون بحسب ترتيب معالجة الملفات، ويتضمن أيضًا الملفات التي لا تحوي على نتائج وهو ما لا يهمنا في هذه الحالة، يمكن حل المشكلة الأخيرة بسهولة عبر تصفية الملفات التي تحوي 0 نتائج كالتالي:
$ grep -irwc --include='*.js' Opal . | grep -v ':0$'
ولترتيب النتائج يمكننا تمرير النتيجة النهائية إلى أمر الترتيب sort
كالتالي:
$ grep -irwc --include='*.js' Opal . | grep -v ':0$' | sort -t: -k2n
يمكنك الرجوع إلى دليل الأمر sort
لمعرفة معاني الخيارات التي استخدمناها في المثال السابق.
10. إيجاد الفرق بين نتيجتي بحث
استخدمنا في أحد الأمثلة السابقة الكلمة ".Opal"، في حال بحثنا في نفس تلك الملفات هذه المرة عن الكلمة "Opal," سنحصل على عشرين نتيجة إضافية:
$ grep -irw --include='*.js' Opal . | wc -l 86 $ grep -ir --include='*.js' Opal . | wc -l 105
يمكننا استخراج الفرق بين عمليتي البحث، وما هي الأسطر التي تحتوي على أحرف كلمة opal الأربعة متفرقة من دون أن تشكل كلمة واحدة؟، فمن الصعب الإجابة على هذا السؤال لأنه يمكن لنفس السطر أن يحوي كلًا من كلمة opal وكلمة أكبر منها تحوي حروفها الأربعة، يمكن محاولة الإجابة على هذا السؤال مبدأيًا بتمرير النتيجة إلى أمر آخر كما فعلنا في مثال سابق:
$ grep -ir --include='*.js' Opal . | grep -ivw Opal ./npm/examples.js: const opalBuilder = OpalBuilder.create(); ./npm/examples.js: opalBuilder.appendPaths('build/asciidoctor/lib'); ./npm/examples.js: opalBuilder.appendPaths('lib'); ...
يمكنك أيضًا محاولة البحث واستكشاف استخدامات الكائن opalBuilder
بنفسك.
ختامًا
تنفيذك لعدد من أوامر grep
ضمن مشروع جديد لن يمكنك من فهم ترتيب ومعمارية المشروع وبنية الشيفرات ضمنه، ولكن ستتمكن من إجراء تحليلات بسيطة مشابهة للأمثلة التي ذكرناها تفيدك في التعرف على المشاريع الجديدة، رأينا قوة الأمر grep
واستخداماته المتعددة التي يمكنك إضافتها إلى مهاراتك.
ترجمة -وبتصرف- للمقال 10 Practical Grep Command Examples for Developers لصاحبه Sylvain Leroux.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.