La commande la plus courte pour calculer la somme d'une colonne de sortie sur Unix?

StackOverflow https://stackoverflow.com/questions/295781

  •  08-07-2019
  •  | 
  •  

Question

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 me vienne à 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
Était-ce utile?

La solution

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 obtenir des résultats arbitrairement longs (crédits attribués à Jouni K. ):

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

Autres conseils

J'essaierais de construire une chaîne de calcul et de l'envoyer à bc comme suit:

  1. grep les lignes contenant les nombres
  2. sed élimine 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 adaptez les blancs aux caractères '+'
  5. bon appétit bc !

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

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

Si bc n'est pas installé, vous pouvez utiliser les 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.

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

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

Exemple avec séparateur de champ personnalisé:

<*>

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 souhaitez traiter stdin dans le cadre d'une police, 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

One-Liner:

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

Je sais que cette question est un peu dépassée, mais je ne peux pas voir "mon". répondez ici, alors j'ai décidé de poster néanmoins. J'irais avec une combinaison de

  • tail (pour obtenir les lignes dont vous avez besoin)
  • tr (pour réduire plusieurs espaces consécutifs à un seul
  • )
  • couper (pour obtenir uniquement la colonne requise)
  • paste (pour concaténer chaque ligne avec un signe + )
  • bc (pour faire le calcul)

ipcs ne génère pas de sortie sur mon système, je vais donc en faire la démonstration 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.

Tous les éléments de cette solution ont été montrés dans les autres réponses, mais jamais dans cette combinaison.

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

Vous devriez alors pouvoir canaliser cela dans grep , en supprimant les caractères non numériques.

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

Si vous avez utilisé tr pour remplacer les nouvelles lignes ( \ n ) par des espaces ( ), et l'a acheminé via 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 qui ressemble à ce qui suit:

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

Les indicateurs cut sont peut-être un peu incorrects, mais man est votre ami:)

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}'

Merci pour le one-liner Python ci-dessus !. Cela m'a aidé à vérifier facilement l'espace utilisé sur mon disque. Voici un one-liner mixte shell / Python qui le fait - compte l’espace utilisé sur le périphérique / dev / sda en mégaoctets. Il m'a fallu un certain temps avant de le découvrir, 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 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])"

Merci encore!

Pour additionner des valeurs dans une colonne, vous pouvez utiliser GNU datamash. Comme les quatre premières lignes ne contiennent pas de valeurs que vous souhaitez résumer, nous les supprimons avec tail +4 .

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

L'option -W définit le délimiteur de champ sur (éventuellement plusieurs) espaces blancs.

Si vous souhaitez additionner plusieurs colonnes spécifiques, vous pouvez utiliser:

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

qui fonctionnera si vous souhaitez additionner les colonnes 1 à 5.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top