Successivo: , Precedente: , Su: Leggere file   [Contenuti][Indice]


4.4 Cambiare il contenuto di un campo

Il contenuto di un campo, così come è visto da awk, può essere cambiato all’interno di un programma awk; questo cambia quello che awk percepisce come record in input corrente. (Il reale file in input non viene toccato; awk non modifica mai il file in input). Si consideri il seguente esempio e il suo output:

$ awk '{ numero_pacchi = $3 ; $3 = $3 - 10
>        print numero_pacchi, $3 }' inventory-shipped
-| 25 15
-| 32 22
-| 24 14
…

Il programma per prima cosa salva il valore originale del campo tre nella variabile numero_pacchi. Il segno ‘-’ rappresenta la sottrazione, così questo programma riassegna il campo tre, $3, come il valore originale del campo meno dieci: ‘$3 - 10’. (Vedi la sezione Operatori aritmetici.) Poi stampa il valore originale e quello nuovo del campo tre. (Qualcuno nel magazzino ha fatto un errore ricorrente nell’inventariare le scatole rosse.)

Perché questo funzioni, il testo in $3 deve poter essere riconosciuto come un numero; la stringa di caratteri dev’essere convertita in un numero affiché il computer possa eseguire operazioni aritmetiche su di essa. Il numero che risulta dalla sottrazione viene nuovamente convertito in una stringa di caratteri che quindi diventa il campo tre. Vedi la sezione Conversione di stringhe e numeri.

Quando il valore di un campo è cambiato (come percepito da awk), il testo del record in input viene ricalcolato per contenere il nuovo campo al posto di quello vecchio. In altre parole, $0 cambia per riflettere il campo modificato. Questo programma stampa una copia del file in input, con 10 sottratto dal secondo campo di ogni riga:

$ awk '{ $2 = $2 - 10; print $0 }' inventory-shipped
-| Jan 3 25 15 115
-| Feb 5 32 24 226
-| Mar 5 24 34 228
…

È possibile inoltre assegnare contenuti a campi che sono fuori intervallo. Per esempio:

$ awk '{ $6 = ($5 + $4 + $3 + $2)
>        print $6 }' inventory-shipped
-| 168
-| 297
-| 301
…

Abbiamo appena creato $6, il cui valore è la somma dei campi $2, $3, $4 e $5. Il segno ‘+’ rappresenta l’addizione. Per il file inventory-shipped, $6 rappresenta il numero totale di pacchi spediti in un determinato mese.

La creazione di un nuovo campo cambia la copia interna di awk nel record in input corrente, che è il valore di $0. Così, se si scrive ‘print $0’ dopo aver aggiunto un campo, il record stampato include il nuovo campo, col numero di separatori di campo appropriati tra esso e i campi originariamente presenti.

Questa ridefinizione influenza ed è influenzata da NF (il numero dei campi; vedi la sezione Un’introduzione ai campi). Per esempio, il valore di NF è impostato al numero del campo più elevato che è stato creato. Il formato preciso di $0 è influenzato anche da una funzionalità che non è ancora stata trattata: il separatore di campo di output, OFS, usato per separare i campi (vedi la sezione I separatori di output e come modificarli).

Si noti, comunque, che il mero riferimento a un campo fuori intervallo non cambia il valore di $0 o di NF. Far riferimento a un campo fuori intervallo produce solo una stringa nulla. Per esempio:

if ($(NF+1) != "")
    print "non è possibile"
else
    print "è tutto normale"

dovrebbe stampare ‘è tutto normale’, perché NF+1 è certamente fuori intervallo. (Vedi la sezione L’istruzione if-else per maggiori informazioni sulle istruzioni if-else di awk. Vedi la sezione Tipi di variabile ed espressioni di confronto per maggiori informazioni sull’operatore ‘!=’.)

È importante notare che facendo un assegnamento a un campo esistente cambia il valore di $0 ma non cambia il valore di NF, anche qualora si assegni a un campo la stringa nulla. Per esempio:

$ echo a b c d | awk '{ OFS = ":"; $2 = ""
>                       print $0; print NF }'
-| a::c:d
-| 4

Il campo è ancora lì; ha solo un valore vuoto, delimitato dai due "due punti" tra ‘a’ e ‘c’. Questo esempio mostra cosa succede se si crea un nuovo campo:

$ echo a b c d | awk '{ OFS = ":"; $2 = ""; $6 = "nuovo"
>                       print $0; print NF }'
-| a::c:d::nuovo
-| 6

Il campo intermedio, $5, è creato con un valore vuoto (indicato dalla seconda coppia di due punti (:) adiacenti), e NF è aggiornato col valore sei.

Decrementando NF si eliminano i campi dopo il nuovo valore di NF e si ricalcola $0. (a.b.) Vediamo un esempio:

$ echo a b c d e f | awk '{ print "NF =", NF;
>                           NF = 3; print $0 }'
-| NF = 6
-| a b c

ATTENZIONE: Alcune versioni di awk non ricostruiscono $0 quando NF viene diminuito. Fino ad agosto 2018, fra queste c’era BWK awk; per fortuna da allora la sua versione funziona correttamente.

Infine, ci sono casi in cui conviene forzare awk a ricostruire l’intero record, usando i valori correnti dei campi e OFS. Per far ciò, si usa l’apparentemente innocuo assegnamento:

$1 = $1   # forza la ricostruzione del record
print $0  # o qualsiasi altra cosa con $0

Questo forza awk a ricostruire il record. Aggiungere un commento rende tutto più chiaro, come abbiamo appena visto.

C’è un rovescio della medaglia nella relazione tra $0 e i campi. Qualsiasi assegnamento a $0 fa sì che il record sia rianalizzato (sintatticamente) e ridiviso in campi usando il valore corrente di FS. Questo si applica anche a qualsiasi funzione predefinita che aggiorna $0, come sub() e gsub() (vedi la sezione Funzioni di manipolazione di stringhe).

Comprendere $0

È importante ricordare che $0 è l’intero record, esattamente com’è stato letto dall’input, compresi tutti gli spazi vuoti iniziali e finali, e l’esatto spazio vuoto (o altri caratteri) che separa i campi.

È un errore comune tentare di cambiare il separatore di campo in un record semplicemente impostando FS e OFS, e poi aspettarsi che un semplice ‘print’ or ‘print $0’ stampi il record modificato.

Questo non funziona, poiché non è stato fatto niente per cambiare quello stesso record. Invece, si deve forzare la ricostruzione del record, tipicamente con un’istruzione come ‘$1 = $1’, come descritto in precedenza.


Successivo: , Precedente: , Su: Leggere file   [Contenuti][Indice]