دوال awk وتنسيق printf وإنشاء التقارير
عرّف دوال مخصّصة واستخدم printf لإخراج تقارير جدولية منسّقة من تدفقات البيانات الخام
دوال awk وتنسيق printf وإنشاء التقارير درس مجاني في Linux Command Line & Bash Scripting Mastery على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Linux Command Line & Bash Scripting Mastery، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Linux Command Line & Bash Scripting Mastery 4 دروس في المجموع.
أهمية الدوال وprintf في awk
مع نمو برامج awk من أوامر في سطر واحد إلى مسارات متعددة الخطوات، تصبح ميزتان أساسيتان: الدوال التي يعرّفها المستخدم وتنسيق printf.
تتيح لك الدوال تغليف المنطق القابل لإعادة الاستخدام — مثل حاسبة النسب المئوية، أو أداة قص السلاسل النصية، أو محوّل الوحدات — بحيث تكتبه مرة واحدة وتستدعيه في أي مكان ضمن البرنامج نفسه. وتتيح لك printf التحكم الدقيق في مظهر المخرجات: عروض الأعمدة، والمنازل العشرية، والحشو، والمحاذاة.
تحوّل هاتان الميزتان معًا أسطر السجلات الخام إلى تقارير مصقولة وسهلة القراءة يمكن للمهندسين والمديرين اتخاذ الإجراءات بناءً عليها.
- تقلل الدوال التكرار وتجعل اختبار البرامج بمعزل عن بقية الأجزاء ممكنًا.
- تُحاذي
printfالبيانات في أعمدة بعرض ثابت، لذلك تظل التقارير واضحة حتى مع اختلاف أطوال المدخلات. - تعمل الميزتان كلتاهما في POSIX awk وgawk وmawk — من دون الحاجة إلى امتدادات.
تعريف دالة للمستخدم في awk
يُصرَّح عن دالة المستخدم باستخدام الكلمة المفتاحية function، قبل أي قواعد نمط-إجراء أو بعدها. والصياغة هي:
function name(param1, param2, local1, local2) {
# body
return value
}من اصطلاحات awk المهمة: المتغيرات المحلية ليست سوى معاملات إضافية تسبقها مسافات إضافية. فلا توجد كلمة مفتاحية باسم local — إذ تشير اتفاقية استخدام المسافات الإضافية إلى أن هذه المعاملات محلية وليست وسائط يمررها المستدعي.
- تكون جميع المتغيرات التي لم تُعلن كمعاملات عالمية افتراضيًا.
- يمكن للدوال استدعاء نفسها استدعاءً递يًا.
- استخدام
returnاختياري؛ ومن دونه تعيد الدالة سلسلة نصية فارغة.
#!/usr/bin/env bash
# Demonstrate a simple awk user function
echo '10 3
7 0
100 4' | awk '
function divide(a, b, result) {
if (b == 0) return "ERR"
result = a / b
return result
}
{
print $1 "/" $2 " = " divide($1, $2)
}
'الدوال مع منطق السلاسل النصية
تكون الدوال مفيدة خصوصًا عند تكرار عمليات السلاسل النصية. تأمل قص المسافات البيضاء في البداية والنهاية — وهي خطوة تحتاج إليها بيانات السجلات الخام كثيرًا قبل المقارنة أو إعداد التقارير.
يعرّف المثال أدناه trim(s) باستخدام gsub، ويستخدمها في كل سجل، مما يحافظ على نظافة القاعدة الرئيسية وسهولة قراءتها.
- تعدّل
gsub(regex, replacement, target)قيمة target مباشرةً وتعيد عدد الاستبدالات. - إن وضع منطق التعبير النمطي داخل دالة يُبقي كتلة القاعدة مركزة على منطق العمل.
- يمكنك إجراء اختبار وحدات بتمرير سلاسل نصية مُعدّة يدويًا مباشرةً داخل كتلة
BEGIN.
#!/usr/bin/env bash
# trim() strips leading/trailing spaces; used to normalise CSV-like data
printf ' alice , 90 \n bob , 85 \n carol , 92 \n' | awk -F',' '
function trim(s) {
gsub(/^[ \t]+|[ \t]+$/, "", s)
return s
}
{
name = trim($1)
score = trim($2)
print name, score
}
'مقدمة إلى printf في awk
تتبع printf في awk الاصطلاحات نفسها المتبعة في C وprintf المضمّنة في Bash. والفرق الأساسي عن print هو أن printf لا تضيف سطرًا جديدًا تلقائيًا مطلقًا — بل يجب أن تضيف \n بنفسك.
محدِّدات التنسيق الشائعة:
%s— سلسلة نصية%d— عدد صحيح%f— عدد عشري%e— تدوين علمي%g— الأقصر بين%f/%e
يتحكم رقما العرض والدقة بوضع أرقام بين % ومحدِّد التنسيق: إذ تجعل %-20s المحاذاة إلى اليسار ضمن حقل من 20 محرفًا؛ بينما تعطي %8.2f عددًا عشريًا بعرض 8 محارف ومنزلتين عشريتين.
#!/usr/bin/env bash
# printf format specifiers in awk
echo '' | awk '
BEGIN {
printf "%s\n", "plain string"
printf "%10s\n", "right-pad"
printf "%-10s|\n", "left-pad"
printf "%8.2f\n", 3.14159
printf "%08d\n", 42
printf "%e\n", 123456.789
}
'إنشاء عنوان تقرير باستخدام printf
يحتاج التقرير المصقول إلى صف عناوين وسطر فاصل. وتُعد كتلة BEGIN المكان الطبيعي لإخراجهما — فهي تُنفَّذ مرة واحدة قبل معالجة أي مدخلات.
تكمن الحيلة في مطابقة العروض في العنوان تمامًا للعروض المستخدمة في صفوف البيانات. ويساعد تعريف ثوابت العرض في BEGIN (أو في دالة) على إبقاء كل شيء متناسقًا عند تعديل عروض الأعمدة لاحقًا.
- استخدم
printfمع سلسلة فاصلة من الشرطات لرسم الخط الفاصل. - اختم أسطر العنوان دائمًا بـ
\n. - اجمع بين عنوان في
BEGINوصفوف لكل سجل وتذييل فيENDلإنشاء بنية تقرير كاملة.
#!/usr/bin/env bash
# Print a report header in BEGIN, data rows per-record, total in END
printf 'alice 9200 12\nbob 8750 10\ncarol 10400 14\n' | awk '
BEGIN {
printf "%-10s %10s %6s %12s\n", "Name", "Salary", "Months", "Annual"
printf "%s\n", "----------------------------------------------"
total = 0
}
{
annual = $2 * $3
total += annual
printf "%-10s %10d %6d %12d\n", $1, $2, $3, annual
}
END {
printf "%s\n", "----------------------------------------------"
printf "%-10s %10s %6s %12d\n", "TOTAL", "", "", total
}
'دوال المستخدم لمنطق التنسيق
عند إنشاء تقارير متعددة الأعمدة، من الشائع أن تحتاج إلى استدعاء التنسيق نفسه في عدة مواضع — مثل عرض شريط نسبة مئوية أو تحويل البايتات إلى وحدة سهلة القراءة. إن وضع ذلك داخل دالة يتجنب التكرار ويسهّل تغيير التنسيق على مستوى البرنامج بأكمله.
تحوّل الدالة أدناه البايتات إلى KB أو MB أو GB، وتعيد سلسلة نصية منسّقة. وتمرر القاعدة المستدعية ببساطة $NF (الحقل الأخير)، ثم تطبع القيمة المعادة.
#!/usr/bin/env bash
# human_bytes() converts raw byte counts to KB/MB/GB strings
printf 'var.log 1024\naccess.log 2097152\ncore.dump 1073741824\ntiny.txt 512\n' | awk '
function human_bytes(n, s) {
if (n >= 1073741824) { s = sprintf("%.1f GB", n/1073741824) }
else if (n >= 1048576) { s = sprintf("%.1f MB", n/1048576) }
else if (n >= 1024) { s = sprintf("%.1f KB", n/1024) }
else { s = sprintf("%d B", n) }
return s
}
BEGIN { printf "%-20s %10s\n", "File", "Size"; print "------------------------------" }
{ printf "%-20s %10s\n", $1, human_bytes($2) }
'تجميع البيانات في المصفوفات قبل الطباعة
غالبًا ما تحتاج التقارير الواقعية إلى الإجماليات أو المتوسطات أو الترتيب — وهي قيم لا يمكنك حسابها قبل رؤية كل المدخلات. ويكون النمط كما يلي:
- تجميع البيانات في المصفوفات الترابطية أثناء مرحلة معالجة كل سجل.
- حساب القيم المشتقة (مثل المتوسطات والنسب المئوية) داخل
END. - إخراج النتائج المنسقة من
ENDفقط، حتى تتمكن عروض الأعمدة من التكيف مع مجموعة البيانات بأكملها.
يُعد نمط تأجيل الإخراج هذا من أقوى الأساليب الاصطلاحية في إنشاء التقارير باستخدام awk. يحصي المثال أدناه عدد الطلبات وعدد بايتات الاستجابة لكل رمز حالة HTTP من سجل وصول.
#!/usr/bin/env bash
# Summarise an nginx-style access log by HTTP status code
printf '127.0.0.1 200 1234\n127.0.0.1 404 512\n10.0.0.2 200 8900\n10.0.0.3 500 256\n10.0.0.4 200 4096\n10.0.0.5 404 300\n' | awk '
{
status = $2
bytes = $3
count[status]++
total_bytes[status] += bytes
}
END {
printf "%-8s %8s %14s\n", "Status", "Requests", "Total Bytes"
print "-----------------------------------"
for (s in count)
printf "%-8s %8d %14d\n", s, count[s], total_bytes[s]
}
'الدوال التكرارية في awk
يدعم awk التكرار الذاتي. ومن الاستخدامات الكلاسيكية لذلك حساب المضروبات أو إنشاء سلاسل متكررة (مثل سطر من الشرطات بطول يساوي عرض الطرفية). يقل استخدام التكرار الذاتي في awk مقارنةً باللغات عامة الغرض، لكنه يعمل بصورة صحيحة ومتاح في جميع التطبيقات الرئيسية.
يعرّف المثال أدناه الدالة التكرارية repeat(s, n) ويستخدمها لرسم خطوط فاصلة تطابق تلقائيًا عرض أوسع صف بيانات — من دون الحاجة إلى شرطات ذات طول ثابت.
- تجنب التكرار الذاتي العميق عند معالجة مدخلات كبيرة — فلا يتوفر في awk تحسين لاستدعاءات الذيل.
- للتكرار البسيط، يكون استخدام
sprintf("%*s", n, "")معgsub(/ /, "-")أسرع، لكنه أقل توضيحًا.
#!/usr/bin/env bash
# Recursive repeat() to build separator lines dynamically
awk '
function repeat(s, n, acc) {
if (n <= 0) return ""
acc = s repeat(s, n-1)
return acc
}
BEGIN {
header = sprintf("%-15s %10s %10s", "Metric", "Current", "Target")
sep = repeat("-", length(header))
print sep
print header
print sep
printf "%-15s %10.1f %10.1f\n", "CPU %", 72.4, 60.0
printf "%-15s %10.1f %10.1f\n", "Mem GB", 14.2, 16.0
printf "%-15s %10d %10d\n", "Open FDs", 1024, 800
print sep
}
' /dev/nullاستخدام printf مع ملف أو مسار أنابيب داخل awk
يمكن لـ printf (وكذلك print) في awk إعادة توجيه الإخراج إلى ملفات أو تمريره عبر أنابيب إلى أوامر shell — مباشرةً من داخل برنامج awk، من دون تغليف البرنامج بأكمله داخل subshell.
printf "..." > "file.txt"— يكتب إلى ملف (يقتطع محتواه مرة واحدة، ثم يضيف إليه أثناء التشغيل نفسه).printf "..." >> "file.txt"— يضيف إلى ملف.printf "..." | "sort -rn"— يمرر الإخراج إلى أمر shell؛ ويحافظ awk على إبقاء مسار الأنابيب مفتوحًا عبر السجلات، ثم يغلقه عند انتهاء البرنامج.
من الأنماط الشائعة تقسيم المرور الواحد على سجل إلى عدة ملفات إخراج بحسب الحالة أو المضيف، لتجنب إعادة فحص ملف كبير عدة مرات.
#!/usr/bin/env bash
# Route records into per-status files using print redirection
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
printf '200 /index.html\n404 /missing\n200 /api/data\n500 /crash\n404 /lost\n' | awk -v out="$tmpdir" '
{
file = out "/" $1 ".log"
printf "%-6s %s\n", $1, $2 > file
}
END {
close(file) # flush all open handles
}
'
echo "=== 200 ==="
cat "$tmpdir/200.log"
echo "=== 404 ==="
cat "$tmpdir/404.log"إنشاء تقرير CSV باستخدام awk
ليست كل التقارير موجهة إلى البشر. ففي بعض الأحيان يجب أن يكون الإخراج بصيغة CSV قابلة للقراءة آليًا، لتغذية جدول بيانات أو مسار معالجة لاحق. يتعامل printf في awk مع ذلك بسهولة: عيّن OFS إلى فاصلة، أو تحكم في كل فاصل بشكل صريح.
قاعدتان مهمتان لإنشاء CSV متين باستخدام awk:
- ضع الحقول التي قد تحتوي على فواصل أو أسطر جديدة بين علامتي اقتباس مزدوجتين.
- اهرب أي علامات اقتباس مزدوجة حرفية داخل الحقل بمضاعفتها:
He said ""hello"".
تغلف الدالة csv_field(s) أدناه منطق وضع علامات الاقتباس هذا، بحيث يمكن تطبيقه باستمرار على كل حقل نصي.
#!/usr/bin/env bash
# Emit a valid CSV report from space-separated input
printf 'alice engineer 95000\nbob "sales,mgr" 82000\ncarol developer 110000\n' | awk '
function csv_field(s, safe) {
safe = s
gsub(/"/, "\"\"" , safe) # double any embedded quotes
return "\"" safe "\"" # wrap in quotes
}
BEGIN { print "Name,Role,Salary" }
{
printf "%s,%s,%d\n", csv_field($1), csv_field($2), $3
}
'من البداية إلى النهاية: تقرير كامل لسجل الوصول
يجمع هذا المثال النهائي كل شيء معًا: الدوال التي يعرّفها المستخدم، وتنسيق printf، وتجميع البيانات في المصفوفات، وترويسة وتذييل منظمان. الإدخال هو سجل وصول مبسط إلى الويب؛ أما الإخراج فهو جدول ملخص مقروء للبشر، يتضمن إجماليات لكل طريقة وسطرًا للإجمالي الكلي.
التقنيات الأساسية المستخدمة:
- يجمع
count[method]++وbytes[method] += sizeإحصاءات كل طريقة في مرور واحد. - تحوّل
human_bytes()(من مشهد سابق) إجماليات البايتات. - يضمن استخدام
printfمع عروض متطابقة في الترويسة والبيانات والتذييل بقاء الجدول مصطفًا بصرف النظر عن حجم الإدخال. - تكرّر كتلة
ENDعلى المصفوفة باستخدامfor (k in arr)، وتُخرج النتائج مرتبة عبر مسار أنابيب إلىsort.
#!/usr/bin/env bash
# Full access log report: method breakdown with human-readable bytes
printf 'GET /index 200 4096\nPOST /api 201 512\nGET /img 200 102400\nDELETE /item 204 0\nPOST /login 401 256\nGET /style 200 8192\n' | awk '
function human_bytes(n, s) {
if (n >= 1048576) s = sprintf("%.1f MB", n/1048576)
else if (n >= 1024) s = sprintf("%.1f KB", n/1024)
else s = sprintf("%d B", n)
return s
}
{
method = $1
size = $4
count[method]++
bytes[method] += size
grand_count++
grand_bytes += size
}
END {
fmt = "%-10s %8s %15s\n"
sep = "----------------------------------"
printf fmt, "Method", "Requests", "Bytes"
print sep
for (m in count)
printf "%-10s %8d %15s\n", m, count[m], human_bytes(bytes[m])
print sep
printf "%-10s %8d %15s\n", "TOTAL", grand_count, human_bytes(grand_bytes)
}
'اختبار المعرفة: المتغيرات المحلية في دوال awk
أي من الخيارات التالية يعلن result على أنه متغير محلي داخل دالة awk، وفقًا لاصطلاح POSIX؟
مراجعة الدرس: دوال awk وprintf وإنشاء التقارير
تعلمت في هذا الدرس كيفية كتابة برامج awk بجودة مناسبة للإنتاج، تنشئ تقارير منسقة انطلاقًا من تدفقات البيانات الخام. إليك ما ينبغي الاحتفاظ به:
- يُعلن عن الدوال التي يعرّفها المستخدم باستخدام
function name(params, locals). تُعد المسافة البيضاء الإضافية قبل معاملات المتغيرات المحلية اصطلاح POSIX — ولا توجد كلمة مفتاحية باسمlocal. - يوفر printf تحكمًا دقيقًا في الإخراج: العرض (
%10s)، والمحاذاة (%-10s)، والدقة (%8.2f). تذكر إضافة\nبشكل صريح. - استخدم BEGIN لإخراج الترويسات والفواصل، وقواعد كل سجل لتجميع البيانات، وEND لحساب الإجماليات وطباعة التقرير المكتمل.
- أعد توجيه إخراج
printfإلى الملفات (> file) أو مسارات الأنابيب (| "sort") من داخل awk لتقسيم التقارير أو معالجة الإخراج لاحقًا. - ضع الحقول النصية داخل دالة مساعدة باسم csv_field() عند إخراج CSV، للتعامل بأمان مع الفواصل وعلامات الاقتباس المضمّنة.
- يجدر الاحتفاظ بالدوال التي تحوّل الوحدات (
human_bytes) أو تكرر المحارف (repeat) أو تنظف السلاسل النصية (trimوcsv_field) في مكتبة awk شخصية — إذ يمكن تركيبها بسلاسة عبر المشاريع المختلفة.
الأسئلة الشائعة
هل درس «دوال awk وتنسيق printf وإنشاء التقارير» مجاني؟
نعم — نص درس «دوال awk وتنسيق printf وإنشاء التقارير» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Linux Command Line & Bash Scripting Mastery، انتقل إلى CoddyKit PRO. تتضمن دورة Linux Command Line & Bash Scripting Mastery 4 دروس في المجموع.
ماذا ستتعلم في «دوال awk وتنسيق printf وإنشاء التقارير»؟
عرّف دوال مخصّصة واستخدم printf لإخراج تقارير جدولية منسّقة من تدفقات البيانات الخام تتمرن على Linux Command Line & Bash Scripting Mastery مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Linux Command Line & Bash Scripting Mastery؟
لا تُشترط خبرة سابقة. Linux Command Line & Bash Scripting Mastery على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «دوال awk وتنسيق printf وإنشاء التقارير»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Linux Command Line & Bash Scripting Mastery هذا؟
نعم. كل درس في Linux Command Line & Bash Scripting Mastery يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- السجلات والحقول والفواصل المخصّصة في awk
- الأنماط والنطاقات وكتل BEGIN وEND
- التجميع باستخدام مصفوفات awk والتصنيف
- دوال awk وتنسيق printf وإنشاء التقارير