Przydatne parsowanie liczb z przyrostkami jednostek?

10

Załóżmy, że masz dane o ilościach w formacie czytelnym dla człowieka, takie jak dane wyjściowe du -hi chcesz dalej operować na tych liczbach. Załóżmy, że chcesz przesłać dane przez grep, aby dokonać podsumowania podzbioru tych danych. Robisz to ad hoc na wielu systemach, których nigdy wcześniej nie widziałeś, i posiadasz tylko minimalne narzędzia. Chcesz konwersji sufiksów dla wszystkich standardowych sufiksów 10 ^ n.

Czy istnieje narzędzie gnu-linux do konwertowania sufiksowanych liczb na liczby rzeczywiste w potoku? Czy masz napisaną do tego funkcję bash, czy jakiś perl, który może być łatwy do zapamiętania, zamiast długiej wymiany wyrażeń regularnych lub kilku kroków 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}'


Odpowiednie referencje:

fasolki
źródło
2
Rzadko musisz używać grep i awk. Jeśli używasz awk, użyj awk. Wystarczy dodać /200907/przed kodzie za linię, npawk '/200907/{s+=$1} END {print s}'
Tony

Odpowiedzi:

14

Na podstawie mojej odpowiedzi na jedno z pytań, do których linkujesz:

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

Inna stosowana metoda:

sed 's/G/ * 1000 M/;s/M/ * 1000 K/;s/K/ * 1000/; s/$/ +\\/; $a0' | bc
Wstrzymano do odwołania.
źródło
w przypadku drugiej metody, co jeśli sufiksem jest s?
djuarez
@djuarez: Jaki mnożnik oznacza skrót s?
Wstrzymano do odwołania.
Brak, tylko ekstrapolacja innych przypadków jednostkowych.
djuarez
@djuarez: To nie ma sensu. Ta odpowiedź dotyczy sufiksów SI, a nie jednostek ogólnych (może sekund?). Aby rozszerzyć sedpolecenie w mojej odpowiedzi, należy dodać klauzule do obsługi dodatkowych sufiksów SI, jak pokazuję w awkpoleceniu. s/T/ * 1000 G;dodane na początku dodają na przykład terabajty.
Wstrzymano do odwołania.
3

Możesz użyć do tego wyrażeń regularnych perla. Na przykład,

$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;
}

To jest prosty skrypt. Możesz uznać to za punkt wyjścia. Mam nadzieję, że to pomoże!

Khaled
źródło
Rzeczywiście, jest to jeden sposób. Ja również znaleźć stackoverflow.com/questions/2557649/... .
fasola
3

Osobiście po prostu nie używałbym flagi -h. Wersja „czytelna dla człowieka” zaokrągla liczby, które trzeba będzie ponownie zaokrąglić po ponownej konwersji, uzyskując jeszcze mniejszą dokładność. (Na przykład 2,7 Mb to 2831155.2 bajtów. Co zrobiłeś z pozostałymi 0,8 bajtu ??!)

W przeciwnym razie możesz poprosić unitso konwersję MiB / GiB / KiB do samego „B”, a on sobie z tym poradzi, ale musisz zrobić coś takiego (zakładając, że twój wynik jest tabulowany, w przeciwnym razie cutodpowiednio)

{your output} | cut -f1 '-d{tab}' | xargs -L 1 -I {} units -1t {}iB B | awk '{s+=$1}END{printf "%d\n",s}'
DerfK
źródło
Dobrze zauważono, że występuje utrata precyzji. Uzupełnianie danych wejściowych do jednostek również działa .. ale unitsbrakowało mi na mojej minimalnej dystrybucji! Myślę, że wszyscy zrobilibyśmy to inaczej, gdybyśmy mieli pełną kontrolę nad wszystkim.
fasola
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
źródło