¿El comando más corto para calcular la suma de una columna de salida en Unix?
Pregunta
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 script 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 mostrar 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
Solución
ipcs -mb | tail +4 | awk '{ sum += $7 } END { print sum }'
O sin cola:
ipcs -mb | awk 'NR > 3 { sum += $7 } END { print sum }'
Uso de awk con bc para obtener resultados largos arbitrarios (créditos para Jouni K.
):
ipcs -mb | awk 'NR > 3 { print $7 }' | paste -sd+ | bc
Otros consejos
Intentaría construir una cadena de cálculo y alimentarla a bc de la siguiente manera:
- grep las líneas que contienen los números
- sed ausente todos los caracteres antes (y después) del número en cada línea
- xargs el resultado (para obtener una cadena de números separados por espacios en blanco)
- tr responde los espacios en blanco a los caracteres '+'
- buen apetito bc !
Parece que esto es un poco más largo que la solución awk , pero para todos los que no pueden leer (y entender) el código extraño awk , esto puede ser más fácil de entender ... :-)
Si bc no está instalado, puede usar paréntesis dobles 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 espacio después y antes del paréntesis doble es opcional.
Tengo un script de utilidad que simplemente agrega todas columnas. Por lo general, es bastante fácil obtener el que desea de la salida de una línea. Como beneficio adicional, se reconocen algunos sufijos SI.
#!/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 "";
}
Ejemplo con separador de campo personalizado:
<*>Solución 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 la entrada estándar como parte de una canalización, use
import sys
total = 0
for line in sys.stdin:
...etc...
Si quiere 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
One-liner:
import sys; print sum( [int(line.split()[6]) for line in sys.stdin.splitlines()[3:]] )
Sé que esta pregunta está algo anticuada, pero no puedo ver "mi" Respondo aquí, así que decidí publicar. Me gustaría ir con una combinación de
- cola (para obtener las líneas que necesita)
- tr (para reducir múltiples espacios consecutivos a uno)
- cortar (para obtener solo la columna necesaria)
- pegar (para concatenar cada línea con un signo
+
) - bc (para hacer el cálculo real)
ipcs
no da una salida en mi sistema, así que lo demostraré 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 en particular en mi sistema realmente no tiene sentido, pero muestra el concepto.
Todas las partes de esta solución se han mostrado en las otras respuestas, pero nunca en esa combinación.
Podría comenzar ejecutando los datos a través de cut
, que al menos recortaría las columnas.
Debería poder canalizar eso en grep
, eliminando los no numéricos.
Entonces ... bueno, entonces no estoy seguro. Podría ser posible canalizar eso a bc
. De lo contrario, sin duda podría entregarse a un script de shell para agregar cada elemento.
Si usó tr
para cambiar las nuevas líneas ( \ n
) a espacios (
), y lo canalizó a través de xargs en su script que bucles hasta que no haya más entradas, agregando cada una, puede tener 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
Es posible que las banderas cut
estén un poco mal, pero man
es tu amigo :)
Puede buscarlo en cualquier referencia awk en línea:
ipcs | awk '
BEGIN { sum = 0 }
/0x000000/ { sum = sum + $2 }
END {print sum}'
¡Gracias por la línea de Python de arriba !. Me ayudó a verificar fácilmente el espacio usado en mi disco. Aquí hay una línea mixta de shell / Python, que hace esto: cuenta el espacio utilizado en el dispositivo / dev / sda en megabytes. Me tomó algo de tiempo, antes de descubrirlo, así que tal vez alguien también encuentre esto útil.
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()])"
o más Python / menos 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])"
¡Gracias de nuevo!
Para sumar valores en una columna, puede usar GNU datamash. Como las primeras cuatro líneas no contienen valores que desea resumir, las eliminamos con tail +4
.
ipcs -mb | tail +4 | datamash -W sum 7
La opción -W
establece el delimitador de campo en (posiblemente múltiples) espacios en blanco.
Si tiene columnas específicas y múltiples que desea sumar, puede usar:
input_command | awk '{s1+=$1;s2+=$2;s3+=$3;s4+=$4;s5+=$5}END{print s1,s2,s3,s4,s5}'
que funcionará si desea sumar las columnas 1 & # 8211; 5.