[AWK e C++] programma per estrarre dei dati usando un ciclo for

Linguaggi di programmazione: php, perl, python, C, bash e tutti gli altri.
Mihira
Prode Principiante
Messaggi: 37
Iscrizione: mercoledì 3 settembre 2008, 16:35

[AWK e C++] programma per estrarre dei dati usando un ciclo for

Messaggio da Mihira »

Ciao a tutti,
sono alle prime prese con AWK e ho già delle difficoltà...
in pratica devo lavorare su un file di log all'interno del quale mi interessa prendere alcune informazioni di una riga ben precisa che è  questa (ne prendo una a caso di questo tipo per farvi capire):
3 END_DOWNLOAD 5 0 2 0 1 1 2 0 0
           
ecco in pratica io devo cercare nel file di log "sorgente.txt" tutte queste righe e lavorarci su. Nella fattispecie mi avevano consigliato di fare questa istruzione che scansiona l'intero file:

awk '{if($2=="END_DOWNLOAD" && $7==1) print ($1-$8)}' sorgente.txt > destinazione.txt

questa istruzione fa questo: quando il campo n°7 di una riga con "END_DOWNLOAD"  vale 1, fa la differenza tra il primo campo e il settimo scrivendo il risultato sul file "destinazione.txt". Questo comando funziona bene, l'ho provato.
Ecco io adesso dovrei fare un ciclo for che cambi la condizione && $7==1 in una condizione che varia con i=1,2,...10  e cioè && $7==i, cioè mi serve un programmino AWK che scansioni l'intero file scrivendo sul file "destinazione.txt" prima tutte le condizioni in cui && $7==1, poi a seguire tutte le condizioni in cui && $7==2, e così via fino a && $7==10.  Ho provato il seguente codice AWK/C++, ma ho qualche dubbio sull'effettivo funzionamento...non sono sicuro che scansioni il file nel modo che mi serve. Ecco il codice che ho provato:

awk '{for (i=1; i chunk4.txt


la mia perplessità nasce dal fatto che ripetendo le singole istruzioni
awk '{if($2=="END_DOWNLOAD" && $7==1) print ($1-$8)}' sorgente.txt > destinazione.txt
al variare di i=1,2,...,10 ottengo sempre lo stesso risultato (e deve venire così), se faccio il ciclo for il file risultante viene diverso e non è la concatenazione di tutti pezzi uguali...sapete aiutarmi? ???
(vi allego anche il log se volete fare delle prove)

ciao e grazie  ;)
Allegati
sorgente.txt
(8.13 KiB) Scaricato 23 volte
Mihira
Prode Principiante
Messaggi: 37
Iscrizione: mercoledì 3 settembre 2008, 16:35

Re: [AWK e C++] programma per estrarre dei dati usando un ciclo for

Messaggio da Mihira »

nessuno sa usare AWK?  :-\
Avatar utente
takeshi
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 789
Iscrizione: venerdì 24 novembre 2006, 18:33

Re: [AWK e C++] programma per estrarre dei dati usando un ciclo for

Messaggio da takeshi »

awk '{for (i=1; i chunk4.txt
Non sarebbe meglio verificare direttamente che $7 sia compreso tra 1 e 100?

Codice: Seleziona tutto

awk '$2 ~ "END_DOWNLOAD" && ($7<=100 && $7>=1){print $1-$8}' tree.txt
la mia perplessità nasce dal fatto che ripetendo le singole istruzioni
awk '{if($2=="END_DOWNLOAD" && $7==1) print ($1-$8)}' sorgente.txt > destinazione.txt
al variare di i=1,2,...,10 ottengo sempre lo stesso risultato (e deve venire così),
Non so se sia un errore di trascrizione ma quella riga non dipende da i quindi è normale che l'output sia sempre lo stesso.
se faccio il ciclo for il file risultante viene diverso e non è la concatenazione di tutti pezzi uguali...sapete aiutarmi?
Ho fatto delle prove con sorgente.txt e non ho questo problema. Prova di nuovo magari utilizzando un output diverso da $1-$8; per esempio:

Codice: Seleziona tutto

awk '$2 ~ "END_DOWNLOAD" && ($7<=100 && $7>=1){print "riga:" NR "$7:" $7 "diff:" $1-$8}' tree.txt
Magari viene fuori l'inghippo.
Raising Elephants Is So Utterly Boring
Mihira
Prode Principiante
Messaggi: 37
Iscrizione: mercoledì 3 settembre 2008, 16:35

Re: [AWK e C++] programma per estrarre dei dati usando un ciclo for

Messaggio da Mihira »

Ciao,
innanzitutto grazie infinite dell'aiuto  :)
allora provo a spiegarmi più chiaramente, forse non ci ero riuscito nei post precedenti.
Dunque il problema è che io dovrei ordinare all'interno di un file i dati elaborati (il valore della sottrazione $1-$8) in base al campo $7, cioè prima tutti quelli con il campo $7==1, poi ==2, ..., ==10.
L'istruzione che mi hai consigliato non cambia il mio problema, ti spiego il motivo. Se scrivo una alla volta le seguenti istruzioni variando il campo $7 da 1 a 10, ottengo:

andrea@andrea-desktop:~/Scrivania/raccolta_log/tree/7_peer$ awk '$2 ~ "END_DOWNLOAD" && ($7==1){print "riga: " NR " chunk n°: " $7 " ritardo: " $1-$8}' tree.txt
riga: 43 chunk n°: 1 ritardo: 1
riga: 44 chunk n°: 1 ritardo: 1
riga: 58 chunk n°: 1 ritardo: 2
riga: 59 chunk n°: 1 ritardo: 2
riga: 62 chunk n°: 1 ritardo: 2
riga: 63 chunk n°: 1 ritardo: 2
andrea@andrea-desktop:~/Scrivania/raccolta_log/tree/7_peer$ awk '$2 ~ "END_DOWNLOAD" && ($7==2){print "riga: " NR " chunk n°: " $7 " ritardo: " $1-$8}' tree.txt
riga: 60 chunk n°: 2 ritardo: 1
riga: 61 chunk n°: 2 ritardo: 1
riga: 77 chunk n°: 2 ritardo: 2
riga: 78 chunk n°: 2 ritardo: 2
riga: 81 chunk n°: 2 ritardo: 2
riga: 82 chunk n°: 2 ritardo: 2
andrea@andrea-desktop:~/Scrivania/raccolta_log/tree/7_peer$ awk '$2 ~ "END_DOWNLOAD" && ($7==3){print "riga: " NR " chunk n°: " $7 " ritardo: " $1-$8}' tree.txt
riga: 79 chunk n°: 3 ritardo: 1
riga: 80 chunk n°: 3 ritardo: 1
riga: 96 chunk n°: 3 ritardo: 2
riga: 97 chunk n°: 3 ritardo: 2
riga: 100 chunk n°: 3 ritardo: 2
riga: 101 chunk n°: 3 ritardo: 2
andrea@andrea-desktop:~/Scrivania/raccolta_log/tree/7_peer$ awk '$2 ~ "END_DOWNLOAD" && ($7==4){print "riga: " NR " chunk n°: " $7 " ritardo: " $1-$8}' tree.txt
riga: 98 chunk n°: 4 ritardo: 1
riga: 99 chunk n°: 4 ritardo: 1
riga: 115 chunk n°: 4 ritardo: 2
riga: 116 chunk n°: 4 ritardo: 2
riga: 117 chunk n°: 4 ritardo: 2
riga: 118 chunk n°: 4 ritardo: 2
andrea@andrea-desktop:~/Scrivania/raccolta_log/tree/7_peer$ awk '$2 ~ "END_DOWNLOAD" && ($7==5){print "riga: " NR " chunk n°: " $7 " ritardo: " $1-$8}' tree.txt
riga: 119 chunk n°: 5 ritardo: 1
riga: 120 chunk n°: 5 ritardo: 1
riga: 136 chunk n°: 5 ritardo: 2
riga: 137 chunk n°: 5 ritardo: 2
riga: 138 chunk n°: 5 ritardo: 2
riga: 139 chunk n°: 5 ritardo: 2
andrea@andrea-desktop:~/Scrivania/raccolta_log/tree/7_peer$ awk '$2 ~ "END_DOWNLOAD" && ($7==6){print "riga: " NR " chunk n°: " $7 " ritardo: " $1-$8}' tree.txt
riga: 134 chunk n°: 6 ritardo: 1
riga: 135 chunk n°: 6 ritardo: 1
riga: 153 chunk n°: 6 ritardo: 2
riga: 155 chunk n°: 6 ritardo: 2
riga: 157 chunk n°: 6 ritardo: 2
riga: 158 chunk n°: 6 ritardo: 2
andrea@andrea-desktop:~/Scrivania/raccolta_log/tree/7_peer$ awk '$2 ~ "END_DOWNLOAD" && ($7==7){print "riga: " NR " chunk n°: " $7 " ritardo: " $1-$8}' tree.txt
riga: 154 chunk n°: 7 ritardo: 1
riga: 156 chunk n°: 7 ritardo: 1
riga: 173 chunk n°: 7 ritardo: 2
riga: 174 chunk n°: 7 ritardo: 2
riga: 175 chunk n°: 7 ritardo: 2
riga: 176 chunk n°: 7 ritardo: 2
andrea@andrea-desktop:~/Scrivania/raccolta_log/tree/7_peer$ awk '$2 ~ "END_DOWNLOAD" && ($7==8){print "riga: " NR " chunk n°: " $7 " ritardo: " $1-$8}' tree.txt
riga: 172 chunk n°: 8 ritardo: 1
riga: 177 chunk n°: 8 ritardo: 1
riga: 191 chunk n°: 8 ritardo: 2
riga: 192 chunk n°: 8 ritardo: 2
riga: 195 chunk n°: 8 ritardo: 2
riga: 196 chunk n°: 8 ritardo: 2
andrea@andrea-desktop:~/Scrivania/raccolta_log/tree/7_peer$ awk '$2 ~ "END_DOWNLOAD" && ($7==9){print "riga: " NR " chunk n°: " $7 " ritardo: " $1-$8}' tree.txt
riga: 193 chunk n°: 9 ritardo: 1
riga: 194 chunk n°: 9 ritardo: 1
riga: 211 chunk n°: 9 ritardo: 2
riga: 212 chunk n°: 9 ritardo: 2
riga: 213 chunk n°: 9 ritardo: 2
riga: 214 chunk n°: 9 ritardo: 2
andrea@andrea-desktop:~/Scrivania/raccolta_log/tree/7_peer$ awk '$2 ~ "END_DOWNLOAD" && ($7==10){print "riga: " NR " chunk n°: " $7 " ritardo: " $1-$8}' tree.txt
riga: 209 chunk n°: 10 ritardo: 1
riga: 210 chunk n°: 10 ritardo: 1
riga: 225 chunk n°: 10 ritardo: 2
riga: 226 chunk n°: 10 ritardo: 2
riga: 227 chunk n°: 10 ritardo: 2
riga: 228 chunk n°: 10 ritardo: 2

ed è giusto da un punto di vista teorico che io ottenga quei risultati per i ritardi, tutti uguali per ogni valore del chunk (il campo $7). Se invece adopero l'istruzione che mi proponi, similmente a quanto accade col ciclo for,  riottengo lo stesso problema, cioè i valori dei ritardi (la sottrazione $1-$8) sono corretti ma non sono ordinati in ordine crescente in base al valore del campo $7 (che sarebbe il chunk ID). Se faccio l'istruzione seguente ottengo infatti:
andrea@andrea-desktop:~/Scrivania/raccolta_log/tree/7_peer$ awk '$2 ~ "END_DOWNLOAD" && ($7=1){print "riga: " NR " chunk n°: " $7 " ritardo: " $1-$8}' tree.txt
riga: 43 chunk n°: 1 ritardo: 1
riga: 44 chunk n°: 1 ritardo: 1
riga: 58 chunk n°: 1 ritardo: 2
riga: 59 chunk n°: 1 ritardo: 2
riga: 60 chunk n°: 2 ritardo: 1
riga: 61 chunk n°: 2 ritardo: 1
riga: 62 chunk n°: 1 ritardo: 2
riga: 63 chunk n°: 1 ritardo: 2
riga: 77 chunk n°: 2 ritardo: 2
riga: 78 chunk n°: 2 ritardo: 2
riga: 79 chunk n°: 3 ritardo: 1
riga: 80 chunk n°: 3 ritardo: 1
riga: 81 chunk n°: 2 ritardo: 2
riga: 82 chunk n°: 2 ritardo: 2
riga: 96 chunk n°: 3 ritardo: 2
riga: 97 chunk n°: 3 ritardo: 2
riga: 98 chunk n°: 4 ritardo: 1
riga: 99 chunk n°: 4 ritardo: 1
riga: 100 chunk n°: 3 ritardo: 2
riga: 101 chunk n°: 3 ritardo: 2
riga: 115 chunk n°: 4 ritardo: 2
riga: 116 chunk n°: 4 ritardo: 2
riga: 117 chunk n°: 4 ritardo: 2
riga: 118 chunk n°: 4 ritardo: 2
riga: 119 chunk n°: 5 ritardo: 1
riga: 120 chunk n°: 5 ritardo: 1
riga: 134 chunk n°: 6 ritardo: 1
riga: 135 chunk n°: 6 ritardo: 1
riga: 136 chunk n°: 5 ritardo: 2
riga: 137 chunk n°: 5 ritardo: 2
riga: 138 chunk n°: 5 ritardo: 2
riga: 139 chunk n°: 5 ritardo: 2
riga: 153 chunk n°: 6 ritardo: 2
riga: 154 chunk n°: 7 ritardo: 1
riga: 155 chunk n°: 6 ritardo: 2
riga: 156 chunk n°: 7 ritardo: 1
riga: 157 chunk n°: 6 ritardo: 2
riga: 158 chunk n°: 6 ritardo: 2
riga: 172 chunk n°: 8 ritardo: 1
riga: 173 chunk n°: 7 ritardo: 2
riga: 174 chunk n°: 7 ritardo: 2
riga: 175 chunk n°: 7 ritardo: 2
riga: 176 chunk n°: 7 ritardo: 2
riga: 177 chunk n°: 8 ritardo: 1
riga: 191 chunk n°: 8 ritardo: 2
riga: 192 chunk n°: 8 ritardo: 2
riga: 193 chunk n°: 9 ritardo: 1
riga: 194 chunk n°: 9 ritardo: 1
riga: 195 chunk n°: 8 ritardo: 2
riga: 196 chunk n°: 8 ritardo: 2
riga: 209 chunk n°: 10 ritardo: 1
riga: 210 chunk n°: 10 ritardo: 1
riga: 211 chunk n°: 9 ritardo: 2
riga: 212 chunk n°: 9 ritardo: 2
riga: 213 chunk n°: 9 ritardo: 2
riga: 214 chunk n°: 9 ritardo: 2
riga: 225 chunk n°: 10 ritardo: 2
riga: 226 chunk n°: 10 ritardo: 2
riga: 227 chunk n°: 10 ritardo: 2
riga: 228 chunk n°: 10 ritardo: 2

come vedi i ritardi sono corretti ma andrebbero ordinati in base al numero del chunk e non in base alla riga che viene incontrata per prima, quindi dovrebbero essere prima tutti quelli con chunk n°1, poi quelli con n°2, ..., fino a quelli col numero 10. E' possibile fare una cosa del genere? Si deve creare una funzione del tipo "estrai(i)"

awk '$2 ~ "END_DOWNLOAD" && ($7>=i){print "riga: " NR " chunk n°: " $7 " ritardo: " $1-$8}' tree.txt

che scriva all'interno dello stesso file "sorgente.txt" (in append mi sa a questo punto) e che poi venga richiamata da un ciclo for il cui indice scorre da 1 a 10? Oppure c'è un modo più semplice per fare tutto questo?
grazie di tutto ;)
Ultima modifica di Mihira il lunedì 29 settembre 2008, 12:13, modificato 1 volta in totale.
Avatar utente
takeshi
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 789
Iscrizione: venerdì 24 novembre 2006, 18:33

Re: [AWK e C++] programma per estrarre dei dati usando un ciclo for

Messaggio da takeshi »

Ci sono (almeno) due modi di fare ciò che ti serve:

1)Usi solo awk leggendo il file una volta e tenendo traccia dei risultati trovati usando degli array associativi; alla fine, stampi il tutto mediante un ciclo for:

Codice: Seleziona tutto

#!/usr/bin/awk -f
$2 ~ "END_DOWNLOAD" && ($7<=MAX && $7>=1){
	if (cache[$7])
		cache[$7]=cache[$7]"\n"$1-$8
	else
		cache[$7]=$1-$8;
} 
END{
	for (i=1;i<=MAX;i++) 
		print cache[i]
}
da chiamare con:

Codice: Seleziona tutto

awk -v MAX=10 -f script.awk sorgente.txt
2)sempre leggendo una sola volta il file, stampi tutto a casaccio ma con $7 all'inizio ogni riga. In questo modo ti basta usare una pipe prima verso sort e poi verso cut (per togliere $7):

Codice: Seleziona tutto

#!/usr/bin/awk -f 
$2 ~ "END_DOWNLOAD" && ($7<=MAX && $7>=1){
	print $7,$1-$8;
}
e poi ordini l'output:

Codice: Seleziona tutto

awk -v MAX=10 -f script2.awk sorgente.txt | sort -n -k1 | cut -d\  -f2
Dovrebbero funzionare...
Raising Elephants Is So Utterly Boring
Mihira
Prode Principiante
Messaggi: 37
Iscrizione: mercoledì 3 settembre 2008, 16:35

Re: [AWK e C++] programma per estrarre dei dati usando un ciclo for

Messaggio da Mihira »

ciao  takeshi :)
i tuoi script funzionano alla grande...grazie davvero! ;)
Non ho ben capito da un punto di vista concettuale perchè hai usato in quel modo l'if e l'else e come funzionano questi array associativi (mi riferisco a questo pezzo del programma):

Codice: Seleziona tutto

#!/usr/bin/awk -f
$2 ~ "END_DOWNLOAD" && ($7<=MAX && $7>=1){
	if (cache[$7])
		cache[$7]=cache[$7]"\n"$1-$8
	else
		cache[$7]=$1-$8;
} 
quindi se hai voglia e pazienza di spiegarmelo in modo semplice te ne sarei molto grato, ma se è troppo lungo e complesso lascia stare, fa lo stesso.
Comunque in modo "brutale"  :P sulla scorta di questi script che mi hai proposto ho creato quest'altro script che serve per contare il numero N di volte che ciascun chunk X viene ricevuto:

Codice: Seleziona tutto

$2 ~ "END_DOWNLOAD" && ($7<=MAX && $7>=1){
        if (cache[$7])
                cache[$7]=++cache[$7]"\n"
        else
                cache[$7]=++cache[$7];
} 
END{
        for (i=1;i<=MAX;i++) 
                print "il chunk " i " è stato scaricato " cache[i] " volte"
}
poi da terminale: eseguo il comando
awk -v MAX=10 -f chunkXscaricatoNvolte.awk sorgente.txt > conteggi2.txt

l'unica cosa è che nel file di log conteggi2.txt la parola "volte" mi va accapo, non so perchè:

il chunk 1 è stato scaricato 6
volte
il chunk 2 è stato scaricato 6
volte
il chunk 3 è stato scaricato 6
volte
il chunk 4 è stato scaricato 6
volte
il chunk 5 è stato scaricato 6
volte
il chunk 6 è stato scaricato 6
volte
il chunk 7 è stato scaricato 6
volte
il chunk 8 è stato scaricato 6
volte
il chunk 9 è stato scaricato 6
volte
il chunk 10 è stato scaricato 6
volte

c'è un modo per sistemarlo e avere tutto sulla stessa linea?

ciao ciao e grazie ancora  :)
Avatar utente
takeshi
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 789
Iscrizione: venerdì 24 novembre 2006, 18:33

Re: [AWK e C++] programma per estrarre dei dati usando un ciclo for

Messaggio da takeshi »

Mihira ha scritto: i tuoi script funzionano alla grande...grazie davvero! ;)
Di nulla ;)
Non ho ben capito da un punto di vista concettuale perchè hai usato in quel modo l'if e l'else e come funzionano questi array associativi (mi riferisco a questo pezzo del programma):

Codice: Seleziona tutto

#!/usr/bin/awk -f
$2 ~ "END_DOWNLOAD" && ($7<=MAX && $7>=1){
	if (cache[$7])
		cache[$7]=cache[$7]"\n"$1-$8
	else
		cache[$7]=$1-$8;
} 
L'array associativo è sostanzialmente un array in cui l'indice può essere una stringa. Quindi ha senso effettuare dichiarazioni del tipo:

numero["capre"]=12
record["nome"]="guido"

ed ha senso poi andare a rileggerseli in un secondo tempo. Nello script che ho postato li uso solo in senso lato perché in definitiva $7 è un numero però la soluzione è valida anche in casi più generali:

Codice: Seleziona tutto

#!/usr/bin/awk
NF{
	for (i=1;i<=NF;i++)
		if ($i ~ /^[[:alnum:]]+$/)
			cache[$i]++;
}
END{
	for (el in cache)
		print el,cache[el];
}
Questo scriptino stampa quante volte appare ognuna delle parole presenti in un file di testo.

Il codice che ho postato la volta scorsa inizialmente non prevedeva l'IF ma poi l'ho dovuto mettere per evitare di inserire un carattere di newline nel primo caso, quando ancora la variabile cache[$7] non è definita e quindi è nulla (infatti in awk una variabile non definita ha sempre un valore ben definito pari alla stringa vuota che se necessario viene convertita in uno zero:

Codice: Seleziona tutto

$ awk 'BEGIN {print ">" foo ":" bar+1}'
>:1
)
l'unica cosa è che nel file di log conteggi2.txt la parola "volte" mi va accapo, non so perchè:
Va a capo perché gli dici tu di farlo:

Codice: Seleziona tutto

                cache[$7]=++cache[$7]"\n"
"\n" infatti viene sostituito con il carattere "newline". Inoltre assegnare a cache[$7] il suo valore incrementato è un po' ridondante e, anche se probabilmente non ci si guadagna neanche un millisecondo, risulta più leggibile scrivere direttamente:

Codice: Seleziona tutto

$2 ~ "END_DOWNLOAD" && ($7<=MAX && $7>=1){
                cache[$7]++
} 
END{
        for (i=1;i<=MAX;i++) 
                print "il chunk " i " è stato scaricato " cache[i] " volte"
}
Già che c'ero ho tolto pure l'IF :)

Spero di essere stato un po' più chiaro stavolta.
Raising Elephants Is So Utterly Boring
Scrivi risposta

Ritorna a “Programmazione”

Chi c’è in linea

Visualizzano questa sezione: 0 utenti iscritti e 2 ospiti