Análise útil para números com sufixos de unidade?

10

Digamos que você tenha dados com quantidades em formato legível por humanos, como a saída de du -h, e deseje continuar operando com esses números. Digamos que você deseja canalizar seus dados através do grep para fazer uma soma de um subconjunto desses dados. Você faz isso ad-hoc em muitos sistemas que você nunca viu antes e possui apenas utilitários mínimos. Você deseja conversões de sufixo para todos os sufixos padrão de 10 ^ n.

Existe um utilitário gnu-linux para converter os números com sufixo em números reais dentro de um pipeline? Você tem uma função bash escrita para fazer isso, ou algum perl que pode ser fácil de lembrar, em vez de várias substituições de expressões regulares ou várias etapas sed?

38M     /var/crazyface/courses/200909-90147
2.7M    /var/crazyface/courses/200909-90157
1.1M    /var/crazyface/courses/200909-90159
385M    /var/crazyface/courses/200909-90161
1.3M    /var/crazyface/courses/200909-90169
376M    /var/crazyface/courses/200907-90171
8.0K    /var/crazyface/courses/200907-90173
668K    /var/crazyface/courses/200907-90175
564M    /var/crazyface/courses/200907-90178
4.0K    /var/crazyface/courses/200907-90179

| grep 200907 | <amazing suffix conversion> | awk '{s+=$1} END {print s}'


Referências relevantes:

feijões
fonte
2
Você raramente precisa usar grep e awk. Se você estiver usando o awk, use o awk. Basta adicionar /200907/na frente do seu código por linha, por exemploawk '/200907/{s+=$1} END {print s}'
Tony

Respostas:

14

Com base na minha resposta em uma das perguntas às quais você vinculou:

awk '{
    ex = index("KMGTPEZY", substr($1, length($1)))
    val = substr($1, 0, length($1) - 1)

    prod = val * 10^(ex * 3)

    sum += prod
}
END {print sum}'

Outro método usado:

sed 's/G/ * 1000 M/;s/M/ * 1000 K/;s/K/ * 1000/; s/$/ +\\/; $a0' | bc
Pausado até novo aviso.
fonte
para o segundo método, e se o sufixo for s?
Djarez 18/06/19
@djuarez: Qual multiplicador os s representam?
Pausado até novo aviso.
Nenhum, apenas extrapolando em outros casos de unidade.
Djarez 18/06/19
@djuarez: Isso não faz nenhum sentido. Esta resposta é sobre sufixos SI, não unidades gerais (segundos, talvez?). Para estender o sedcomando na minha resposta, você adicionaria cláusulas para lidar com sufixos SI adicionais, como mostro no awkcomando. s/T/ * 1000 G;adicionado no início adicionaria terabytes, por exemplo.
Pausado até novo aviso.
3

Você pode usar expressões regulares perl para fazer isso. Por exemplo,

$value = 0;
if($line =~ /(\d+\.?\d*)(\D+)\s+/) {
   $amplifier = 1024 if ($2 eq 'K');
   $amplifier = 1024 * 1024 if ($2 eq 'M');
   $amplifier = 1024 * 1024 * 1024 if ($2 eq 'G');
   $value = $1 * $amplifier;
}

Este é um script simples. Você pode considerá-lo como ponto de partida. Espero que ajude!

Khaled
fonte
De fato, esse é um caminho. Eu também encontrei stackoverflow.com/questions/2557649/… .
beans
3

Pessoalmente, eu não usaria a bandeira -h em primeiro lugar. A versão "legível por humanos" arredonda números que precisarão ser arredondados novamente quando você converter novamente, ficando ainda menos precisos. (Por exemplo, 2.7MiB é 2831155.2 bytes. O que você fez com os outros 0,8th de um byte ??!)

Caso contrário, você pode pedir unitspara converter MiB / GiB / KiB em apenas "B" e isso resolverá isso, mas você teria que fazer algo como (supondo que sua saída seja tabulada, caso contrário, cutadequadamente)

{your output} | cut -f1 '-d{tab}' | xargs -L 1 -I {} units -1t {}iB B | awk '{s+=$1}END{printf "%d\n",s}'
DerfK
fonte
Bem observado, há uma perda de precisão. Complementar a entrada para as unidades também funciona .. mas encontrei a unitsfalta na minha distribuição mínima! Acho que todos faríamos isso de forma diferente se tivéssemos controle total de tudo.
beans
2
VALUE=$1

for i in "g G m M k K"; do
        VALUE=${VALUE//[gG]/*1024m}
        VALUE=${VALUE//[mM]/*1024k}
        VALUE=${VALUE//[kK]/*1024}
done

[ ${VALUE//\*/} -gt 0 ] && echo VALUE=$((VALUE)) || echo "ERROR: size invalid, pls enter correct size"
S471
fonte