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 que 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
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 en 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:
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 impair awk , cela peut être plus facile à saisir ... :-)
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 ' ' +) ))
ouSUM=$(( $(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 tout 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: $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 champ 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
Bon mot:
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 vois pas "ma" réponse ici, alors j'ai décidé de poster néanmoins. J'irais avec une combinaison de
+
)ipcs
ne donne pas de sortie sur mon système, je vais donc 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 pièces 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 pouvoir canaliser cela dans grep
, en éliminant les 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 modifier les nouvelles lignes (\n
) en espaces () et que vous avez transmis cela par le biais de xargs dans votre script qui boucle jusqu'à ce qu'il n'y ait plus d'entrées, en ajoutant chacune, vous pouvez obtenir 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 drapeaux 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 effectue cette opération - 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 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!
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.
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}'
ce qui fonctionnera si vous voulez additionner les colonnes 1 à 5.