أقصر أمر لحساب مجموع عمود الإخراج على يونكس؟
سؤال
أنا متأكد من أن هناك طريقة سريعة وسهلة لحساب مجموع عمود من القيم على أنظمة يونكس (باستخدام شيء مثل awk
أو xargs
ربما)، ولكن كتابة برنامج نصي لتحليل الصفوف سطرًا تلو الآخر هو الشيء الوحيد الذي يتبادر إلى الذهن في الوقت الحالي.
على سبيل المثال، ما هي أبسط طريقة لتعديل الأمر أدناه لحساب وعرض الإجمالي لعمود SEGSZ (70300)؟
ipcs -mb | head -6
IPC status from /dev/kmem as of Mon Nov 17 08:58:17 2008
T ID KEY MODE OWNER GROUP SEGSZ
Shared Memory:
m 0 0x411c322e --rw-rw-rw- root root 348
m 1 0x4e0c0002 --rw-rw-rw- root root 61760
m 2 0x412013f5 --rw-rw-rw- root root 8192
المحلول
ipcs -mb | tail +4 | awk '{ sum += $7 } END { print sum }'
وأو بدون الذيل:
ipcs -mb | awk 'NR > 3 { sum += $7 } END { print sum }'
وعن طريق AWK مع قبل الميلاد للحصول على نتائج طويلة التعسفية (ائتمانات لJouni K.
):
ipcs -mb | awk 'NR > 3 { print $7 }' | paste -sd+ | bc
نصائح أخرى
سأحاول إنشاء سلسلة حسابية وإطعامها قبل الميلاد على النحو التالي:
- grep الخطوط التي تحتوي على الأرقام
- سيد قم بإزالة جميع الأحرف قبل (وبعد) الرقم الموجود في كل سطر
- com.xargs النتيجة (للحصول على سلسلة من الأرقام مفصولة بالفراغات)
- آر قم بترجمة الفراغات إلى أحرف "+".
- شهية طيبة قبل الميلاد!
ipcs -mb | grep -w '^m ' | sed 's/^.*\s//' | xargs | tr ' ' + | bc
يبدو أن هذا أطول قليلاً من محرج الحل، ولكن لكل من لا يستطيع القراءة (وفهم) الغريب محرج قد يكون هذا الرمز أسهل في الفهم ...:-)
لو قبل الميلاد لم يتم تثبيته، يمكنك استخدام الأقواس المزدوجة في الخطوة 5 أعلاه لحساب النتيجة:
echo $(( $(ipcs -mb | grep -w '^m ' | sed 's/^.*\s//' | xargs | tr ' ' +) ))
أوSUM=$(( $(ipcs -mb | grep -w '^m ' | sed 's/^.*\s//' | xargs | tr ' ' +) ))
أو(( SUM=$(ipcs -mb | grep -w '^m ' | sed 's/^.*\s//' | xargs | tr ' ' +) ))
يعد التباعد بعد وقبل الأقواس المزدوجة أمرًا اختياريًا.
ولدي النصي الأداة التي تضيف ببساطة <م> جميع م> الأعمدة. انها عادة ما تكون السهل بما فيه الكفاية لانتزاع واحدة تريد من إخراج سطر واحد. على سبيل المكافأة، يتم التعرف على بعض SI-اللواحق.
#!/usr/bin/awk -f
# Sum up numerical values by column (white-space separated)
#
# Usage: $0 [file ...]
#
# stern, 1999-2005
{
for(i = 1; i <= NF; ++i) {
scale = 1
if ($i ~ /[kK]$/) { scale = 1000 }
if ($i ~ /[mM]$/) { scale = 1000*1000 }
if ($i ~ /[gG]$/) { scale = 1000*1000*1000 }
col[i] += scale * $i;
}
if (NF > maxnf) maxnf = NF;
}
END {
for(i = 1; i <= maxnf; ++i) { printf " %.10g", col[i] }
print "";
}
وعلى سبيل المثال مع فاصل حقل مخصص:
$ head /etc/passwd | addcol -F:
0 0 45 39 0 0 0
وبيثون الحل
#!/usr/bin/env python
text= file("the_file","r")
total= 0
for line in text:
data = line.split()
if data[0] in ('T', 'Shared', 'IPC'): continue
print line
segsize= int(data[6])
total += segsize
print total
وتوزيعات لينكس أفضل لها بيثون.
إذا كنت تريد معالجة ستدين كجزء من pipline، استخدم
import sys
total = 0
for line in sys.stdin:
...etc...
إذا كنت تريد أن نفترض أن هناك دائما خطوط 3 الرأس:
import sys
total = 0
for line in sys.stdin.readlines()[3:]:
total += int(line.split()[6])
print total
وبطانة الأول:
import sys; print sum( [int(line.split()[6]) for line in sys.stdin.splitlines()[3:]] )
أعلم أن هذا السؤال قديم إلى حد ما، لكن لا يمكنني رؤية إجابتي هنا، لذلك قررت النشر بالرغم من ذلك.سأذهب مع مزيج من
- الذيل (للحصول على الخطوط التي تحتاجها)
- tr (لتقليص مسافات متتالية متعددة إلى واحدة)
- قطع (للحصول على العمود المطلوب فقط)
- لصق (لربط كل سطر بـ a
+
لافتة) - قبل الميلاد (للقيام بالحساب الفعلي)
ipcs
لا يعطي مخرجات على نظامي، لذا سأعرضه فقط df
:
# df
Filesystem 1K-blocks Used Available Use% Mounted on
rootfs 33027952 4037420 27312812 13% /
udev 10240 0 10240 0% /dev
tmpfs 102108 108 102000 1% /run
/dev/xvda1 33027952 4037420 27312812 13% /
tmpfs 5120 0 5120 0% /run/lock
tmpfs 204200 0 204200 0% /run/shm
/dev/xvda1 33027952 4037420 27312812 13% /var/www/clients/client1/web1/log
/dev/xvda1 33027952 4037420 27312812 13% /var/www/clients/client1/web2/log
/dev/xvda1 33027952 4037420 27312812 13% /var/www/clients/client1/web3/log
/dev/xvda1 33027952 4037420 27312812 13% /var/www/clients/client1/web4/log
/dev/xvda1 33027952 4037420 27312812 13% /var/www/clients/client2/web5/log
/dev/xvda1 33027952 4037420 27312812 13% /var/www/clients/client2/web6/log
# df | tail -n +2 | tr -s ' ' | cut -d ' ' -f 2 | paste -s -d+ | bc
264545284
أعلم أن إجراء هذه العملية الحسابية على نظامي ليس له معنى حقًا، ولكنه يوضح المفهوم.
لقد تم عرض جميع أجزاء هذا الحل في الإجابات الأخرى، ولكن لم يتم عرضها أبدًا في تلك المجموعة.
هل يمكن أن تبدأ عن طريق تشغيل البيانات من خلال cut
- التي من شأنها أن ما لا يقل عن تقليم الأعمدة أسفل.
ويجب أن تكون ثم قادرا على الأنابيب التي في grep
، وتجريد المغادرة غير numerics.
وبعد ذلك ... حسنا، ثم لست متأكدا. قد يكون من الممكن أن الأنابيب إلى bc
. إن لم يكن، فإنه يمكن بالتأكيد أن تسلم إلى شيل لإضافة كل عنصر.
إذا كنت تستخدم tr
لتغيير أسطر جديدة (\n
) إلى مسافات ()، والأنابيب أنه من خلال xargs في السيناريو الخاص بك حلقات حتى لا توجد المزيد من المدخلات، وأضاف كل واحد، قد يكون لديك الحل.
وهكذا، شيء أقرب إلى ما يلي:
cat <whatever> | cut -d'\t` -f7 | grep -v <appropriate-character-class> | tr '\n' ' ' | xargs script-that-adds-arguments
وربما أكون قد أعلام cut
الخطأ قليلا - ولكن man
صديقك:)
هل يمكن البحث عنه في أي إشارة AWK على الانترنت:
ipcs | awk '
BEGIN { sum = 0 }
/0x000000/ { sum = sum + $2 }
END {print sum}'
وشكرا لبيثون بطانة واحدة فوق !. انها ساعدتني لمن السهل التحقق من المساحة المستخدمة على القرص بلدي. هنا هو قذيفة المختلطة / بيثون أونيلينير، أن تفعل هذا - تهمة استخدام مساحة على الجهاز / ديف / حزب العمل الديمقراطي في ميغا بايت. استغرق الأمر مني بعض الوقت، قبل وجدت بها، لذلك، ربما هناك من يرى هذا مفيد للغاية.
df -h -B 1M | grep dev/sda | tr -s ' '| cut -d' ' -f3 |python -c "import sys; print sum([int(num) for num in sys.stdin.readlines()])"
وأو أكثر بيثون / أقل قذيفة:
df -h -B 1M | python -c "import sys; print sum([int(l.split()[2]) for l in sys.stdin.readlines() if '/dev/sda' in l])"
وشكرا مرة أخرى!
لجمع القيم في عمود يمكنك استخدام GNU datamash. منذ الأسطر الأربعة الأولى لا تحتوي على القيم التي تريد خلاصة القول، نحن إزالتها مع tail +4
.
ipcs -mb | tail +4 | datamash -W sum 7
والخيار -W
يحدد محدد الحقل إلى (ربما متعددة) الفراغات.
إذا كان لديك، أعمدة متعددة المحددة التي تريد جمع، يمكنك استخدام:
input_command | awk '{s1+=$1;s2+=$2;s3+=$3;s4+=$4;s5+=$5}END{print s1,s2,s3,s4,s5}'
والتي ستعمل إذا كنت تريد جمع أعمدة 1-5.