comando mais curto para calcular a soma de uma coluna de saída no Unix?
Pergunta
Eu tenho certeza que é uma maneira rápida e fácil de calcular a soma de uma coluna de valores em sistemas Unix (usando algo como awk
ou xargs
talvez), mas escrever um script shell para analisar a linha de linhas por linha é a única coisa que vem à mente no momento.
Por exemplo, qual é a maneira mais simples de modificar o comando a seguir para calcular e exibir o total para a coluna 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
Solução
ipcs -mb | tail +4 | awk '{ sum += $7 } END { print sum }'
ou sem cauda:
ipcs -mb | awk 'NR > 3 { sum += $7 } END { print sum }'
Usando o awk com bc ter arbitrárias longos resultados (créditos para Jouni K.
):
ipcs -mb | awk 'NR > 3 { print $7 }' | paste -sd+ | bc
Outras dicas
Gostaria de tentar construir uma seqüência de cálculo e alimentá-lo para bc como segue:
- grep as linhas que contêm os números
- sed de distância todos os caracteres antes (e depois) o número em cada linha
- xargs o resultado (para obter uma seqüência de números separados por espaços em branco)
- tr anslate os espaços em branco para caracteres '+'
- bom apetite bc !
ipcs -mb | grep -w '^m ' | sed 's/^.*\s//' | xargs | tr ' ' + | bc
Parece que este é ligeiramente mais longo do que o awk solução, mas para todos aqueles que não podem ler (e entender) o estranho awk Código isso pode ser mais fácil de entender ...: -)
Se bc não está instalado, você pode usar parênteses duplos no passo 5 acima para calcular o resultado:
-
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 ' ' +) ))
O espaçamento antes e após os parênteses duplos é opcional.
Eu tenho um script utilitário que simplesmente acrescenta-se todas colunas. Geralmente é fácil o suficiente para pegar o que deseja a partir da saída de uma linha. Como um bônus, alguns SI-sufixos são reconhecidos.
#!/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 "";
}
Exemplo com separador de campo personalizado:
$ head /etc/passwd | addcol -F:
0 0 45 39 0 0 0
Python Solução
#!/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
distros Linux têm mais Python.
Se você quiser stdin processo como parte de um pipline, uso
import sys
total = 0
for line in sys.stdin:
...etc...
Se você quiser assumir que há sempre linhas 3 de cabeçalho:
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:]] )
Eu sei que esta questão é um pouco datada, mas eu não posso ver "meu" resposta aqui, então eu decidi postar no entanto. Eu iria com uma combinação de
- cauda (para obter as linhas que você precisa)
- tr (para encolher múltiplos espaços consequitive a um)
- corte (para obter apenas a coluna necessário)
- colar (para concatenar cada linha com um sinal
+
) - bc (para fazer o cálculo real)
ipcs
não dá uma saída no meu sistema, então eu vou demonstrar isso com 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
Eu sei fazer este cálculo especial no meu sistema realmente não faz sentido, mas mostra o conceito.
Todas as peças desta solução foram mostrados em outras respostas, mas nunca nessa combinação.
Você poderia começar por executar os dados através cut
- que seria, pelo menos aparar as colunas para baixo.
Você deve então ser capaz de tubo que em grep
, despindo-out não-numéricos.
Então ... bem, então eu não tenho certeza. Pode ser possível tubo que a bc
. Se não, ele certamente poderia ser entregue a um shell script para adicionar cada item.
Se você usou tr
para alterar as novas linhas (\n
) para espaços () e canalizada que através xargs em seu script que voltas até que não haja mais entradas, acrescentando cada um deles, você pode ter uma resposta.
Assim, algo parecido com o seguinte:
cat <whatever> | cut -d'\t` -f7 | grep -v <appropriate-character-class> | tr '\n' ' ' | xargs script-that-adds-arguments
Eu posso ter as bandeiras cut
ligeiramente errado - mas man
é seu amigo:)
Você poderia procurá-lo em qualquer referência awk on-line:
ipcs | awk '
BEGIN { sum = 0 }
/0x000000/ { sum = sum + $2 }
END {print sum}'
Obrigado pelo Python one-liner acima !. Ele me ajudou a verificar fácil o espaço usado no meu disco. Aqui é um shell mista / Python one-liner, que fazer isso - conta usados ??espaço no dispositivo / dev / sda em megabytes. Levei algum tempo, antes que eu encontrei-o para fora, por isso, talvez alguém acha isso útil também.
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 mais 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])"
Mais uma vez obrigado!
Para somar valores em uma coluna você pode usar GNU datamash. Desde as primeiras quatro linhas não contêm valores que pretende resumir, nós removê-los com tail +4
.
ipcs -mb | tail +4 | datamash -W sum 7
A opção -W
define o delimitador de campo para (possivelmente múltiplas) espaços em branco.
Se você tem específico, várias colunas que você deseja resumir, você pode usar:
input_command | awk '{s1+=$1;s2+=$2;s3+=$3;s4+=$4;s5+=$5}END{print s1,s2,s3,s4,s5}'
que irá funcionar se você deseja somar colunas 1-5.