Análisis práctico para números con sufijos de unidad?

10

Supongamos que tiene datos con cantidades en formato legible para humanos, como la salida de du -h, y desea seguir operando con esos números. Digamos que desea canalizar sus datos a través de grep para hacer una suma de un subconjunto de esos datos. Haces esto ad-hoc en muchos sistemas que nunca has visto antes, y solo tienes utilidades mínimas. Desea conversiones de sufijos para todos los sufijos estándar de 10 ^ n.

¿Existe una utilidad gnu-linux para convertir los números con sufijo a números reales dentro de una tubería? ¿Tiene una función bash escrita para hacer esto, o alguna perl que podría ser fácil de recordar, en lugar de una longitud de reemplazos de expresiones regulares o varios pasos 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}'


Referencias relevantes:

frijoles
fuente
2
Raramente necesita usar grep y awk. Si está usando awk, use awk. Simplemente agregue /200907/delante de su código por línea, por ejemploawk '/200907/{s+=$1} END {print s}'
Tony

Respuestas:

14

Basado en mi respuesta a una de las preguntas que vinculó a:

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}'

Otro método que se usa:

sed 's/G/ * 1000 M/;s/M/ * 1000 K/;s/K/ * 1000/; s/$/ +\\/; $a0' | bc
Pausado hasta nuevo aviso.
fuente
para el segundo método, ¿qué pasa si el sufijo es s?
djuarez
@djuarez: ¿Qué multiplicador representa la s?
Pausado hasta nuevo aviso.
Ninguno, solo extrapolando en otros casos de unidades.
djuarez
@djuarez: Eso no tiene ningún sentido. Esta respuesta es sobre sufijos SI, no unidades generales (¿segundos, tal vez?). Para extender el sedcomando en mi respuesta, agregaría cláusulas para manejar sufijos SI adicionales como lo muestro en el awkcomando. s/T/ * 1000 G;agregado al principio agregaría terabytes, por ejemplo.
Pausado hasta nuevo aviso.
3

Puede usar expresiones regulares perl para hacer esto. Por ejemplo,

$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 es un guión simple. Puedes considerarlo como punto de partida. ¡Espero que ayude!

Khaled
fuente
De hecho, esta es una forma. También he encontrado stackoverflow.com/questions/2557649/… .
frijoles
3

Personalmente, simplemente no usaría la bandera -h en primer lugar. La versión "legible para humanos" redondea los números que deberán redondearse nuevamente cuando convierta de nuevo, siendo cada vez menos precisos. (Por ejemplo, 2.7MiB son 2831155.2 bytes. ¿Qué hiciste con el otro 0.8 de un byte?)

De lo contrario, puede solicitar unitsconvertir MiB / GiB / KiB a solo "B" y se encargará de esto, pero tendría que hacer algo como (suponiendo que su salida esté tabulada, de lo contrario de manera cutadecuada)

{your output} | cut -f1 '-d{tab}' | xargs -L 1 -I {} units -1t {}iB B | awk '{s+=$1}END{printf "%d\n",s}'
DerfK
fuente
Bien notado, que hay una pérdida de precisión. Complementar la entrada a las unidades también funciona ... ¡pero me pareció que unitsfaltaba en mi distribución mínima! Creo que todos haríamos esto de manera diferente si tuviéramos el control total de todo.
frijoles
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
fuente