Domanda

Sono sicuro che esiste un modo semplice e veloce per calcolare la somma di una colonna di valori sui sistemi Unix (usando qualcosa come awk o xargs forse), ma scrivere uno script di shell per analizzare le righe riga per riga è l'unica cosa che viene in mente al momento.

Ad esempio, qual è il modo più semplice per modificare il comando seguente per calcolare e visualizzare il totale per la colonna 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
È stato utile?

Soluzione

ipcs -mb | tail +4 | awk '{ sum += $7 } END { print sum }'

O senza coda:

ipcs -mb | awk 'NR > 3 { sum += $7 } END { print sum }'

Usare awk con bc per ottenere risultati lunghi arbitrari (crediti per Jouni K. ):

ipcs -mb | awk 'NR > 3 { print $7 }' | paste -sd+ | bc

Altri suggerimenti

Vorrei provare a costruire una stringa di calcolo e inviarla a bc come segue:

  1. grep le righe che contengono i numeri
  2. sed elimina tutti i caratteri prima (e dopo) il numero su ciascuna riga
  3. xargs il risultato (per ottenere una stringa di numeri separati da spazi)
  4. tr rispondi agli spazi vuoti con caratteri "+"
  5. buon appetito bc !

ipcs -mb | grep -w '^ m' | sed 's /^.* \ s //' | xargs | tr '' + | bc

Sembra che questo sia leggermente più lungo della soluzione awk , ma per tutti coloro che non sono in grado di leggere (e capire) lo strano codice awk questo potrebbe essere più facile da capire ... :-)

Se bc non è installato, è possibile utilizzare le doppie parentesi nel passaggio 5 sopra per calcolare il risultato:

  • echo $ (($ (ipcs -mb | grep -w '^ m' | sed 's /^.* \ s //' | xargs | tr '' +))) o
  • SUM = $ (($ (ipcs -mb | grep -w '^ m' | sed 's /^.* \ s //' | xargs | tr '' +))) o
  • ((SUM = $ (ipcs -mb | grep -w '^ m' | sed 's /^.* \ s //' | xargs | tr '' +)))

La spaziatura dopo e prima delle doppie parentesi è facoltativa.

Ho uno script di utilità che aggiunge semplicemente tutte colonne. Di solito è abbastanza facile prendere quello che vuoi dall'output di una riga. Come bonus, vengono riconosciuti alcuni suffissi SI.

#!/usr/bin/awk -f
# Sum up numerical values by column (white-space separated)
#
# Usage:  
$ head /etc/passwd | addcol -F:
0 0 45 39 0 0 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 ""; }

Esempio con separatore di campo personalizzato:

<*>

Soluzione Python

#!/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

La maggior parte delle distribuzioni Linux hanno Python.

Se si desidera elaborare stdin come parte di una pipeline, utilizzare

import sys
total = 0
for line in sys.stdin:
   ...etc...

Se vuoi assumere che ci siano sempre 3 righe di intestazione:

import sys
total = 0
for line in sys.stdin.readlines()[3:]:
    total += int(line.split()[6])
print total

One-liner:

import sys; print sum( [int(line.split()[6]) for line in sys.stdin.splitlines()[3:]] )

So che questa domanda è in qualche modo datata, ma non riesco a vedere " my " rispondo qui, così ho deciso di pubblicare comunque. Vorrei andare con una combinazione di

  • tail (per ottenere le linee di cui hai bisogno)
  • tr (per restringere più spazi consequenziali a uno)
  • taglia (per ottenere solo la colonna necessaria)
  • incolla (per concatenare ogni riga con un segno + )
  • bc (per eseguire il calcolo effettivo)

ipcs non fornisce un output sul mio sistema, quindi lo dimostrerò semplicemente con 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

So che fare questo particolare calcolo sul mio sistema non ha davvero senso, ma mostra il concetto.

Tutti i pezzi di questa soluzione sono stati mostrati nelle altre risposte, ma mai in quella combinazione.

Puoi iniziare eseguendo i dati tramite cut , che almeno taglierebbe le colonne verso il basso.

Dovresti quindi essere in grado di reindirizzarlo in grep , eliminando i caratteri non numerici.

Allora ... beh, allora non sono sicuro. Potrebbe essere possibile reindirizzarlo a bc . In caso contrario, potrebbe sicuramente essere passato a uno script di shell per aggiungere ogni elemento.

Se hai usato tr per cambiare le nuove righe ( \ n ) in spazi ( ), e lo hai instradato attraverso xargs nel tuo script che loop fino a quando non ci sono più input, aggiungendo ognuno, potresti avere una risposta.

Quindi, qualcosa di simile al seguente:

cat <whatever> | cut -d'\t` -f7 | grep -v <appropriate-character-class> | tr '\n' ' ' | xargs script-that-adds-arguments

Potrei avere le bandiere cut leggermente sbagliate, ma man è tuo amico :)

Puoi cercarlo in qualsiasi riferimento awk online:

ipcs | awk '
BEGIN { sum = 0 }
/0x000000/ { sum = sum + $2 }
END {print sum}'

Grazie per il one-liner Python sopra !. Mi ha aiutato a controllare facilmente lo spazio utilizzato sul mio disco. Ecco una shell mista / Python one-liner, che fa questo - conta lo spazio utilizzato sul dispositivo / dev / sda in megabyte. Mi ci è voluto del tempo prima che lo scoprissi, quindi forse qualcuno lo trova utile anche.

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()])"

o più shell Python / less:

 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])"

Grazie ancora!

Per sommare i valori in una colonna puoi usare il datamash GNU. Dato che le prime quattro righe non contengono valori che desideri riassumere, le rimuoviamo con tail +4 .

ipcs -mb  | tail +4 | datamash -W sum 7

L'opzione -W imposta il delimitatore di campo su (eventualmente più) spazi bianchi.

Se hai specifiche, più colonne che vuoi sommare, puoi usare:

input_command | awk '{s1+=$1;s2+=$2;s3+=$3;s4+=$4;s5+=$5}END{print s1,s2,s3,s4,s5}'

che funzionerà se vuoi sommare le colonne 1–5.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top