Pregunta ¿El comando más corto para calcular la suma de una columna de salida en Unix?


Estoy seguro de que hay una manera rápida y fácil de calcular la suma de una columna de valores en sistemas Unix (usando algo como awk o xargs quizás), pero escribir un guión de shell para analizar las filas línea por línea es lo único que viene a la mente en este momento.

Por ejemplo, ¿cuál es la forma más sencilla de modificar el siguiente comando para calcular y visualizar el total de la columna 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
2017-11-17 15:05


origen


Respuestas:


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

O sin cola

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

Usar awk con bc para obtener resultados largos arbitrarios (créditos para Jouni K.)

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

79
2017-11-17 15:14



Intentaría construir una cadena de cálculo y alimentarla a antes de Cristo como sigue:

  1. grep las líneas que contienen los números
  2. sed todos los personajes antes (y después) del número en cada línea
  3. xargs el resultado (para obtener una cadena de números separados por espacios en blanco)
  4. tr Añada los espacios en blanco a los caracteres '+'
  5. buen apetito antes de Cristo!

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

Parece que esto es un poco más largo que el awk solución, pero para todos los que no pueden leer (y comprender) lo extraño awk código esto puede ser más fácil de entender ... :-)

Si antes de Cristo no está instalado, puede usar doble paréntesis en el paso 5 anterior para calcular el resultado:

  • 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 ' ' +) ))

El espaciado entre antes y después del doble paréntesis es opcional.


13
2018-06-02 10:50



Tengo un script de utilidad que simplemente suma todas columnas Por lo general, es bastante fácil obtener el que desea de la salida de una línea. Como beneficio adicional, algunos sufijos SI son reconocidos.

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

Ejemplo con separador de campo personalizado:

$ head /etc/passwd | addcol -F:
0 0 45 39 0 0 0

3
2018-02-03 09:28



Solución de 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 mayoría de las distribuciones de Linux tienen Python.

Si desea procesar stdin como parte de una línea de piplínea, use

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

Si desea suponer que siempre hay 3 líneas de encabezado:

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

Un trazador de líneas:

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

2
2017-11-17 15:14



Puede comenzar ejecutando los datos a través de cut - que al menos recortar las columnas.

Entonces deberías ser capaz de canalizar eso en grep, eliminando elementos no numéricos.

Entonces ... bueno, entonces no estoy seguro. Podría ser posible canalizar eso a bc. De lo contrario, podría entregarse a un script de shell para agregar cada elemento.

Si usaste tr para cambiar las nuevas líneas (\n) a espacios (), y canalizó eso a través de xargs en su secuencia de comandos que gira hasta que no haya más entradas, agregando cada una, es posible que tenga una respuesta.

Entonces, algo similar a lo siguiente:

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

Puedo tener el cut banderas ligeramente incorrectas, pero man es tu amigo :)


1
2017-11-17 15:13



Puedes buscarlo en cualquier referencia de awk en línea:

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

1
2017-11-17 15:28



Sé que esta pregunta está un poco anticuada, pero no puedo ver "mi" respuesta aquí, así que decidí publicarla de todos modos. Yo iría con una combinación de

  • cola (para obtener las líneas que necesita)
  • tr (para reducir espacios múltiples consecutivos a uno)
  • corte (para obtener solo la columna necesaria)
  • pegar (para concatenar cada línea con un + firmar)
  • bc (para hacer el cálculo real)

ipcsno da salida a mi sistema, así que solo lo mostraré 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

Sé que hacer este cálculo particular en mi sistema realmente no tiene sentido, pero muestra el concepto.

Todas las piezas de esta solución se han mostrado en las otras respuestas, pero nunca en esa combinación.


1
2018-02-16 22:42