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
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
Je voudrais essayer de construire une chaîne de calcul et le nourrir à bc comme suit:
- grep les lignes qui contiennent les nombres
- sed loin tous les caractères avant (et après) le nombre sur chaque ligne
- xargs - le résultat (pour obtenir une chaîne de nombres séparés par des blancs)
- tr anslate les blancs aux caractères " + "
- 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.
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
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:]] )
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.
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:)
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 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!