Commande la plus courte pour calculer la somme d'une colonne de sortie sous Unix?

Je suis sûr qu'il existe un moyen rapide et facile de calculer la somme d'une colonne de valeurs sur les systèmes Unix (en utilisant quelque chose comme awk ou xargs peut-être), mais écrire un script shell pour analyser les lignes ligne par ligne est la seule chose qui vient à l'esprit pour le moment.

Par exemple, Quel est le moyen le plus simple de modifier la commande ci-dessous pour calculer et afficher le total de la colonne 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
45
demandé sur An̲̳̳drew 2008-11-17 18:05:17

8 réponses

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

Ou sans queue:

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

Utiliser awk avec bc pour avoir des résultats longs arbitraires (crédits à Jouni K.):

ipcs -mb | awk 'NR > 3 { print $7 }' | paste -sd+ | bc
80
répondu Johannes Schaub - litb 2008-11-17 16:06:05

Je voudrais essayer de construire une chaîne de calcul et le nourrir à bc comme suit:

  1. grep les lignes qui contiennent les nombres
  2. sed loin tous les caractères avant (et après) le nombre sur chaque ligne
  3. xargs - le résultat (pour obtenir une chaîne de nombres séparés par des blancs)
  4. tr anslate les blancs aux caractères " + "
  5. bon appétit bc!

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

On dirait que c'est légèrement plus long que la solutionawk , mais pour tous ceux qui ne peuvent pas lire (et comprendre) le code awk Impair, cela peut être plus facile à saisir... :-)

Si bc n'est pas installé, vous pouvez utiliser des doubles parenthèses à l'étape 5 ci-dessus pour calculer le résultat:

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

L'espacement après et avant les doubles parenthèses est facultatif.

13
répondu Peterino 2010-08-25 08:46:56

J'ai un script utilitaire qui ajoute simplement toutes les colonnes. Il est généralement assez facile de saisir celui que vous voulez de la sortie d'une ligne. En bonus, certains suffixes SI sont reconnus.

#!/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 "";
}

Exemple avec un séparateur de champs personnalisé:

$ head /etc/passwd | addcol -F:
0 0 45 39 0 0 0
3
répondu user61853 2009-02-03 09:28:00

Solution 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 plupart des distributions Linux ont Python.

Si vous voulez traiter stdin dans le cadre d'une pipline, Utilisez

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

Si vous voulez supposer qu'il y a toujours 3 lignes d'en-tête:

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

Un-liner:

import sys; print sum( [int(line.split()[6]) for line in sys.stdin.splitlines()[3:]] )
2
répondu S.Lott 2008-11-17 15:27:38

Je sais que cette question Est quelque peu datée, mais je ne peux pas voir" ma " réponse ici, alors j'ai décidé de poster néanmoins. J'irais avec une combinaison de

  • queue (pour obtenir les lignes dont vous avez besoin)
  • tr (pour réduire plusieurs espaces conséquents à un)
  • Couper (pour obtenir uniquement la colonne nécessaire)
  • coller (pour concaténer chaque ligne avec un + signe)
  • bc (pour faire le calcul)

ipcs ne donne pas de sortie sur mon système, donc je vais juste le démo avec 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

Je sais que faire ce calcul particulier sur mon système n'a pas vraiment de sens, mais cela montre le concept.

Toutes les parties de cette solution ont été montrées dans les autres réponses, mais jamais dans cette combinaison.

2
répondu Alexander Stumpf 2015-02-16 22:42:37

Vous pouvez commencer par exécuter les données via cut - ce qui réduirait au moins les colonnes.

Vous devriez alors être capable de canaliser cela dans grep, en éliminant les non-nombres.

Alors ... eh bien, je ne suis pas sûr. Il pourrait être possible de diriger cela vers bc. Sinon, il pourrait certainement être remis à un script shell pour ajouter chaque élément.

Si vous avez utilisé tr pour changer les nouvelles lignes (\n) en espaces (), et canalisé cela à travers xargs dans votre script qui boucle jusqu'à ce qu'il n'y ait plus d'entrées, en ajoutant chacune, vous pouvez avoir une réponse.

Donc, quelque chose s'apparente à ce qui suit:

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

Je peux avoir les drapeaux cut légèrement faux-mais man est votre ami:)

1
répondu warren 2008-11-17 15:13:14

Vous pouvez le rechercher dans n'importe quelle référence awk en ligne:

ipcs | awk '
BEGIN { sum = 0 }
/0x000000/ { sum = sum + $2 }
END {print sum}'
1
répondu florin 2008-11-17 15:28:52

Merci pour le python one-liner ci-dessus!. Il m'a aidé à vérifier l'espace utilisé sur mon disque. Voici un shell mixte / Python one-liner, qui fait ceci - compte l'espace utilisé sur le périphérique / dev / sda en mégaoctets. Il m'a fallu un certain temps, avant que je l'ai découvert, alors, peut-être que quelqu'un trouve cela utile aussi.

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

Ou plus Python / moins shell:

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

Merci encore!

0
répondu 2009-03-13 15:14:22