كيفية إجراء الحساب على ملف السجل
سؤال
لدي الذي يبدو مثل هذا:
I, [2009-03-04T15:03:25.502546 #17925] INFO -- : [8541, 931, 0, 0]
I, [2009-03-04T15:03:26.094855 #17925] INFO -- : [8545, 6678, 0, 0]
I, [2009-03-04T15:03:26.353079 #17925] INFO -- : [5448, 1598, 185, 0]
I, [2009-03-04T15:03:26.360148 #17925] INFO -- : [8555, 1747, 0, 0]
I, [2009-03-04T15:03:26.367523 #17925] INFO -- : [7630, 278, 0, 0]
I, [2009-03-04T15:03:26.375845 #17925] INFO -- : [7640, 286, 0, 0]
I, [2009-03-04T15:03:26.562425 #17925] INFO -- : [5721, 896, 0, 0]
I, [2009-03-04T15:03:30.951336 #17925] INFO -- : [8551, 4752, 1587, 1]
I, [2009-03-04T15:03:30.960007 #17925] INFO -- : [5709, 5295, 0, 0]
I, [2009-03-04T15:03:30.966612 #17925] INFO -- : [7252, 4928, 0, 0]
I, [2009-03-04T15:03:30.974251 #17925] INFO -- : [8561, 4883, 1, 0]
I, [2009-03-04T15:03:31.230426 #17925] INFO -- : [8563, 3866, 250, 0]
I, [2009-03-04T15:03:31.236830 #17925] INFO -- : [8567, 4122, 0, 0]
I, [2009-03-04T15:03:32.056901 #17925] INFO -- : [5696, 5902, 526, 1]
I, [2009-03-04T15:03:32.086004 #17925] INFO -- : [5805, 793, 0, 0]
I, [2009-03-04T15:03:32.110039 #17925] INFO -- : [5786, 818, 0, 0]
I, [2009-03-04T15:03:32.131433 #17925] INFO -- : [5777, 840, 0, 0]
أرغب في إنشاء برنامج نصي لـ Shell يحسب متوسط الحقلين الثاني والثالث بين قوسين (840
و 0
في المثال الأخير).سؤال أصعب:هل من الممكن الحصول على متوسط الحقل الثالث فقط عندما لا يكون الحقل الأخير كذلك 0
?
أعلم أنه يمكنني استخدامها Ruby
أو لغة أخرى لإنشاء برنامج نصي، ولكنني أرغب في القيام بذلك بها Bash
.أي اقتراحات جيدة بشأن الموارد أو تلميحات حول كيفية إنشاء مثل هذا البرنامج النصي من شأنها أن تساعد.
المحلول
نشر الرد الذي ألصقته لك عبر المراسلة الفورية هنا أيضًا، فقط لأنه يجعلني أجرب StackOverflow :)
# replace $2 with the column you want to avg;
awk '{ print $2 }' | perl -ne 'END{ printf "%.2f\n", $total/$n }; chomp; $total+= $_; $n++' < log
نصائح أخرى
يستخدم bash
و awk
:
cat file | sed -ne 's:^.*INFO.*\[\([0-9, ]*\)\][ \r]*$:\1:p' | awk -F ' *, *' '{ sum2 += $2 ; sum3 += $3 } END { if (NR>0) printf "avg2=%.2f, avg3=%.2f\n", sum2/NR, sum3/NR }'
نموذج الإخراج (لبياناتك الأصلية):
avg2=2859.59, avg3=149.94
وبطبيعة الحال، لا تحتاج إلى استخدام cat
, ، تم تضمينه هناك من أجل الوضوح ولتوضيح حقيقة أن بيانات الإدخال يمكن أن تأتي من أي أنبوب؛إذا كان عليك العمل على ملف موجود، قم بتشغيل sed -ne '...' file | ...
مباشرة.
يحرر
إذا كان لديك حق الوصول إلى gawk
(GNU awk)، يمكنك التخلص من الحاجة إلى sed
على النحو التالي:
cat file | gawk '{ if(match($0, /.*INFO.*\[([0-9, ]*)\][ \r]*$/, a)) { cnt++; split(a[1], b, / *, */); sum2+=b[2]; sum3+=b[3] } } END { if (cnt>0) printf "avg2=%.2f, avg3=%.2f\n", sum2/cnt, sum3/cnt }'
نفس الملاحظات إعادة. cat
يتقدم.
قليلا من التوضيح:
sed
طباعة الخطوط فقط (-n ... :p
تركيبة) التي تطابق التعبير العادي (الأسطر التي تحتوي على INFO متبوعة بأي مجموعة من الأرقام والمسافات والفواصل بين قوسين مربعين في نهاية السطر، مما يسمح بمسافات زائدة وCR)؛إذا كان هناك أي سطر متطابق، فاحتفظ فقط بما بين القوسين المربعين (\1
, ، الموافق لما بينهما\(...\)
في التعبير العادي) قبل الطباعة (:p
)- سوف يقوم sed بإخراج الخطوط التي تبدو كما يلي:
8541, 931, 0, 0
- سوف يقوم sed بإخراج الخطوط التي تبدو كما يلي:
awk
يستخدم فاصلة محاطة بمسافات 0 أو أكثر (-F ' *, *'
) كمحددات للحقول؛$1
يتوافق مع العمود الأول (على سبيل المثال.8541)،$2
إلى الثاني الخيتم احتساب الأعمدة المفقودة كقيمة0
- في نهايةالمطاف،
awk
يقسم المجمعاتsum2
الخ حسب عدد السجلات التي تمت معالجتها،NR
- في نهايةالمطاف،
gawk
يفعل كل شيء في طلقة واحدة؛سيختبر أولاً ما إذا كان كل سطر يطابق نفس التعبير العادي الذي تم تمريره في المثال السابق إليهsed
(إلا أنه بخلاف ذلكsed
,awk
لا يتطلب أ\
أمام الأقواس المستديرة لتحديد المناطق أو الاهتمام).إذا تطابق السطر، فإن ما بين الأقواس المستديرة سينتهي في [1]، والذي نقسمه بعد ذلك باستخدام نفس الفاصل (فاصلة محاطة بأي عدد من المسافات) ونستخدمه للتراكم.لقد قدمتcnt
بدلاً من الاستمرار في الاستخدامNR
بسبب عدد السجلات التي تمت معالجتهاNR
قد يكون أكبر من العدد الفعلي للسجلات ذات الصلة (cnt
) إذا لم تكن جميع الأسطر من النموذجINFO ... [...comma-separated-numbers...]
, ، وهو ما لم يكن الحال معهsed|awk
منذsed
ضمان أن كافة الخطوط مرت لawk
كانت ذات صلة.
يستخدم ناوك أو /usr/xpg4/bin/awk على سولاريس.
awk -F'[],]' 'END {
print s/NR, t/ct
}
{
s += $(NF-3)
if ($(NF-1)) {
t += $(NF-2)
ct++
}
}' infile
استخدم بايثون
logfile= open( "somelogfile.log", "r" )
sum2, count2= 0, 0
sum3, count3= 0, 0
for line in logfile:
# find right-most brackets
_, bracket, fieldtext = line.rpartition('[')
datatext, bracket, _ = fieldtext.partition(']')
# split fields and convert to integers
data = map( int, datatext.split(',') )
# compute sums and counts
sum2 += data[1]
count2 += 1
if data[3] != 0:
sum3 += data[2]
count3 += 1
logfile.close()
print sum2, count2, float(sum2)/count2
print sum3, count3, float(sum3)/count3