separador - ¿El comando más corto para calcular la suma de una columna de salida en Unix?
extraer una linea de un archivo en linux (8)
Gracias por el One-Liner de Python anterior !. Me ayudó a verificar fácilmente el espacio usado en mi disco. Aquí hay una shell mixta / Python one-liner, que hace esto: cuenta el espacio utilizado en el dispositivo / dev / sda en megabytes. Me llevó algo de tiempo, antes de darme cuenta, entonces, tal vez alguien encuentre esto útil también.
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 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])"
¡Gracias de nuevo!
Estoy seguro de que hay una manera rápida y fácil de calcular la suma de una columna de valores en los sistemas Unix (utilizando algo como awk
o xargs
), 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
Intentaría construir una cadena de cálculo y pasarla a bc de la siguiente manera:
- grep las líneas que contienen los números
- eliminó 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 ansar los espacios en blanco a los caracteres ''+''
- buen apetito bc !
ipcs -mb | grep -w ''^m '' | sed ''s/^.*/s//'' | xargs | tr '' '' + | bc
Parece que esto es un poco más largo que la solución awk , pero para todos los que no pueden leer (y entienden) el extraño código awk esto puede ser más fácil de entender ... :-)
Si bc 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.
Puede comenzar ejecutando los datos a través del cut
, lo que al menos recortaría las columnas.
Debería poder canalizar eso en grep
, stripping-out no numérico.
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 usó tr
para cambiar las líneas nuevas ( /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
Puede que tenga las banderas cut
poco mal, pero el man
es tu amigo :)
Puedes buscarlo en cualquier referencia de awk en línea:
ipcs | awk ''
BEGIN { sum = 0 }
/0x000000/ { sum = sum + $2 }
END {print sum}''
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 signo
+
) - bc (para hacer el cálculo real)
ipcs
no da salida en 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.
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:]] )
Tengo un script de utilidad que simplemente suma todas las 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
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