awk és a Vodafoneos telefonszámlám

Sziasztok!

Szeretnék írni egy scriptet amiben a havi telefonszámlám van nekem megfelelően részletezve. Vagyis szolgáltatónként a fizetendő összeg, és az összes összeg, valamint ugyan így a beszélt idők, SMS-ek száma is. Pénz összegekkel pont meg is tudtam írni egy sorban gawk segítségével:
gawk '{FS=";"}
{sum += 8}
/Vodafone/ {v += $8}
/Telenor/ {p += $8}
/T-Mobile/ {t += $8}
/Budapest/ {b += $8}
END {print sum}
END {print t,p,v,b}
END {print t+p+v+b}'

A vodafonetól letöltött .csv fájl így kiad 3 sort:
A teljes oszlop összege
Az általam beállított szolgáltatók összege
Szolgáltatók összeadva (ez csak azért kell mert hátha van még valami külföld, egyéb szolgáltató mit tudom én, szóval az első és 3. sornak egyeznie kell)

De mit csináljak a 'Időtartammal'? Ahol hh:mm:ss formátumban vannak az adatok vagy kB-ban az adatforgalom. Hogy lehet összeadni időket awk segítségével? Van rá lehetőség?

Hozzászólások

A kB-ot az idővel összeadni nem érdemes. :-)
Az idők összeadására (és kívonására) kb. 15 évvel ezelőtt írtam függvényeket, és azt használtam. Azóta nem használtam, így nem tudom megígérni, hogy gyorsan megtalálom. De egyszerű a dolog: átalakítod az időket másodpercekre, majd összeadod, és az eredményt visszaalakítod „normál” formátumra.

-----
A kockás zakók és a mellészabások tekintetében kérdezze meg úri szabóját.

Valami ilyesmire kell gondolni:
awk '{split($1, t, ":"); sum+=3600*t[1]+60*t[2]+t[3]} END { print strftime("%H:%M:%S", sum-3600)}' times.txt

kB adatok eldobás:
awk '{split($1, t, ":"); if ($1!=t[1]) sum+=3600*t[1]+60*t[2]+t[3]} END { print strftime("%H:%M:%S", sum-3600)}' times.txt

-----
A kockás zakók és a mellészabások tekintetében kérdezze meg úri szabóját.

Az összeadással nincs baj. A „baj” azzal van, hogy a strftime timestampet vár. Próbáld ki:

echo "" | awk 'END {print(strftime("%H:%M:%S", 0))}'

Ugyan nem íratjuk ki teljesen, de 1970. 01. 01. 01:00:00-t jelent a 0-s timestamp.

-----
A kockás zakók és a mellészabások tekintetében kérdezze meg úri szabóját.

Amikor az írtam, hogy az időszámítás 01:00:00-val kezdődik, abban benne volt, hogy mi a CET időzónában vagyunk. Valóban helyesebb lett volna azt írni, hogy a mi időzónánkban kezdődik 1 órától az időszámítás.

Ha a nyári/téli időszámítás probléma lenne, akkor most 2 órát kellene levonni a helyes értékhez.

Azt is elismerem, hogy korrektebb lenne, ha az strftime függvénnyel előbb lekérdezném az időzónát (%z, %Z), és ez alapján végezném el a korrekciót. Azonban az eredeti feladatból kiindulva, viszonylag kevesen lehetnek, akik egy másik időzónában akarják feldolgozni a magyar szolgáltatótól kapott számla adatait.

Kieg.:
awk '{split($1, t, ":"); if ($1!=t[1]) sum+=3600*t[1]+60*t[2]+t[3]} END { tz=strftime("%z", 0); print strftime("%H:%M:%S", sum-36*tz)}' times.txt

-----
A kockás zakók és a mellészabások tekintetében kérdezze meg úri szabóját.

Az időzóna ilyen kézi manipulálását nem szeretem. Volt, hogy én is így generáltam egy scriptemben listát, s egy egész nap eltűnt a listából a téli-nyári átállás környékén. Ez úgy jött ki, hogy 23 órás lett a nap, azt elosztottam 24-gyel, amelynek az egészrésze 0 lett, így tehát szépen eltűnt egy teljes napom.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

> tz=strftime("%z", 0); print strftime("%H:%M:%S", sum-36*tz)

Ez a kivonogatas sok galibat okozhat.

Sokkal inkabb:

TZ=UTC awk '{.... }'

es nem kell idozonakkal foglalkozni. Sajat peldad:

echo "" | gawk 'END {print(strftime("%H:%M:%S", 0))}'
01:00:00

viszont:

echo "" | TZ=UTC gawk 'END {print(strftime("%H:%M:%S", 0))}'
00:00:00

Ez az echo | awk oly ronda :-) . A BEGIN mintában szereplő dolgokat még /dev/null feldolgozása esetén is végrehajtja az awk:

TZ=UTC gawk 'BEGIN {print strftime("%H:%M:%S", 0) ; }' /dev/null

pont ugyanilyen jó, csak olcsóbb ;-)

(Amúgy az igazán szép az lenne, ha nem kívülről kellene az időzónát piszkálni, de sajnos hiába állítom át az awk-n belülről, nem hajlandó kezelni:

gawk 'BEGIN { ENVIRON["TZ"]="UTC" ; print strftime("%H:%M:%S", 0) ; }' /dev/null

Sőt a gawk és a mawk manuáljában szerepel is - igaz, csak a system-re és a pipe-pal indítot proccesszekre térnek ki, de ettől függetlenül nem működik.)
Ja, mondjuk pont az strftime éppen gawk-only, és mint olyan, nem hordozható.

Számold át másodpercbe, add össze, majd konvertáld vissza óra, perc, másodpercre. Nem tűnik bonyolultnak.

A kB, MB, s effélékre is tudsz táblázatot csinálni, amelyben a szorzó 1024, 1048576 és effélék. Vagy 1000, 1000000. Nem tudom, itt binárisan, vagy SI szerint decimálisan értendő.

Némileg átláthatóbb lenne, ha az awk scriptet nem egy sorba írnád.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Haha, megelőzöm Zahyt: DEHÁT A GAWK NEM IS HORDOZHATÓ!!!!11 :D

miért ragaszkodsz a gawkhoz? mi olyan feature van benne, amit itt használnod _KELL_? az időkkel való zsonglőrködés úgy a leghatásosabb, ha unix timestamppá alakítod őket. sajnos nem vicc.

kilo/mega/gigabájtok konvertálása pedig könnyedén megtanulható mondjuk a 'df' vagy a 'du' forráskódjának tanulmányozásával.

Pontosan. Erre a feladatra bőven elég a "sima" (n)awk. Első ránézésre semmi olyat nem használsz ki, amit akár az (old) awk ne tudna.

Két megjegyzés:

Szerintem a legelső részben nem 8-at, hanem $8-at akarsz a sum-hoz adni.
A második, hogy az END { A ; } END { B; } END { C; } abszolut nem szabványos, de legalább nincs is rá szükség, simán lehet egy db END mintában több utasítás END { A; B; C; } formában.

(Csak azért kötözködök, mert az összes többit már gyakorlatilag megválaszolták :-) )

Tök jól megy, már majdnem megvan minden amit akartam, csak még problémás, hogy egyik oszlopban megtalálható a 'Vodafone' és a 'Vodafone Internet' is. Na most nekem minden olyan sor kéne amiben a 'Vodafone' szerepel. Amit próbáltam és nem megy:
awk -F=';' '/\/ {print}' 0507599656.csv
awk -F=';' '/^Vodafone$/ {print}' 0507599656.csv
gawk -F=";" '$3 == /Vodafone/ {print}' 0507599656.csv
gawk -F=";" '($3 == "Vodafone") {print NR}' 0507599656.csv

Ez persze szépen lefut...
awk -F=';' '/Vodafone\ Internet/ {print}' 0507599656.csv

Találtam egy működő megoldást:
awk -F\; '$3 == "Vodafone" {print}' 0507599656.csv
Mivel a ';' küönleges karakter, kell elé a '\' jel hogy sima karakterként legyen értelmezve.
awk '{FS=";"} $3 == "Vodafone" {print}' 0507599656.csv ez ugyan azt csinálja mint a fenti.
De egy /^Vodafone$/ formát sehogy sem tudok működésre bírni. A sima /Vodafone/ pedig minden olyan sort listáz, amiben szerepel a Vodefone, tehát a Vodefone Internet tartalmú sorokat is.

"De egy /^Vodafone$/ formát sehogy sem tudok működésre bírni."

Mondd, rászántál egy órát arra, hogy elolvasd az awk-programok végrehajtásának menetét? Itt elsősorban a mezőkre bontás és mintaillesztés részekre gondolok?

Másképp kérdezve: tudod, hogy mi a mező, és hogy mire próbál illeszkedni egy konstans reguláris kifejezés, ha nem adod meg konkrétan, hogy mire illeszkedjen?

Hint: illesszünk mintát az n. mezőre, ne az egész bemeneti rekordra!

Elvesztem, hogy ma éppen mi a feladat. Leírnád még egyszer? (Megjegyzések:

- azt írtad, hogy need minden olyan sor kell, amiben a Vodafone bennevan. Mivel a Vodafone Internetet tartalmazó sorban is benne van, szerntem evidens, hogy azt is megtaláklod. Lehet, hogy fenti leírásoddal ellentétben azokat a sorokat nem akarod kiíratni?

- tudom, hogy miért takarod el a ; -t, ez nem gond.

- cserébe nem tudom miért rakod oda a mintáéhoz a { print } részt. Ha nincs AKCIÓ egy awk MINTA mellett, ez az alapértelmezés, tehát simán kihagyhatod.)

Mit szólsz ehhez?

awk '$0 ~ /Vodafone/ && $0 !~ /Vodafone internet/' izemize.ecet.csv

Persze ha mániád hogy mezőkre tördeled a ; mentén, meg hogy csak a 3. mezőben érdekel, akkor ki lehet egészíteni azokkal is.