GNU/Linux >> Linux Esercitazione >  >> Linux

Guida introduttiva al comando AWK [Guida per principianti]

Il comando AWK risale ai primi giorni di Unix. Fa parte dello standard POSIX e dovrebbe essere disponibile su qualsiasi sistema simile a Unix. E oltre.

Sebbene a volte screditato a causa della sua età o della mancanza di funzionalità rispetto a un linguaggio multiuso come Perl, AWK rimane uno strumento che mi piace usare nel mio lavoro quotidiano. A volte per scrivere programmi relativamente complessi, ma anche grazie alle potenti battute che puoi scrivere per risolvere i problemi con i tuoi file di dati.

Quindi, questo è esattamente lo scopo di questo articolo. Mostrandoti come sfruttare la potenza di AWK in meno di 80 caratteri per eseguire attività utili. Questo articolo non vuole essere un tutorial completo di AWK, ma ho comunque incluso alcuni comandi di base all'inizio, quindi anche se hai poca o nessuna esperienza precedente puoi afferrare i concetti di base di AWK.

I miei file di esempio per questo tutorial AWK

Tutti gli one-liner descritti in quell'articolo verranno testati sullo stesso file di dati:

cat file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Puoi ottenere una copia di quel file online su GitHub.

Conoscere le variabili predefinite e automatiche in AWK

AWK supporta un paio di variabili predefinite e automatiche per aiutarti a scrivere i tuoi programmi. Tra questi incontrerai spesso:

RSIl separatore di record. AWK elabora i tuoi dati un record alla volta. Il separatore di record è il delimitatore utilizzato per dividere il flusso di dati di input in record. Per impostazione predefinita, questo è il carattere di nuova riga. Quindi, se non lo modifichi, un record è una riga del file di input.

NRIl numero del record di input corrente. Se stai utilizzando il delimitatore di nuova riga standard per i tuoi record, questo corrisponde al numero di riga di input corrente.

FS/OFSI caratteri utilizzati come separatori di campo. Una volta che AWK legge un record, lo divide in diversi campi in base al valore di FS . Quando AWK stampa un record sull'output, si unirà nuovamente ai campi, ma questa volta, utilizzando il OFS separatore invece di FS separatore. Di solito, FS e OFS sono gli stessi, ma questo non è obbligatorio. "spazio bianco" è il valore predefinito per entrambi.

NF – Il numero di campi nel record corrente. Se stai utilizzando il delimitatore standard "spazio bianco" per i tuoi campi, questo corrisponderà al numero di parole nel record corrente.

Sono disponibili altre variabili AWK più o meno standard, quindi vale la pena controllare il tuo particolare manuale di implementazione AWK per maggiori dettagli. Tuttavia, questo sottoinsieme è già sufficiente per iniziare a scrivere battute interessanti.

A. Utilizzo di base del comando AWK

1. Stampa tutte le righe

Questo esempio è per lo più inutile, ma sarà comunque una buona introduzione alla sintassi AWK:

awk '1 { print }' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

I programmi AWK sono costituiti da uno o più pattern { action } dichiarazioni.

Se, per un dato record ("riga") del file di input, il modello restituisce un valore diverso da zero (equivalente a "true" in AWK), i comandi nel blocco azione corrispondente vengono eseguiti. Nell'esempio sopra, poiché 1 è una costante diversa da zero, il { print } il blocco azione viene eseguito per ogni record di input.

Un altro trucco è { print } è il blocco di azione predefinito che verrà utilizzato da AWK se non ne specifichi uno esplicitamente. Quindi il comando sopra può essere abbreviato come:

awk 1 file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Quasi altrettanto inutile, il seguente programma AWK consumerà il suo input ma non produrrà nulla per l'output:

awk 0 file

2. Rimuovere un'intestazione di file

awk 'NR>1' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Ricorda, questo equivale a scrivere in modo esplicito:

awk 'NR>1 { print }' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Questo one-liner scriverà i record del file di input tranne il primo poiché in tal caso la condizione è 1>1 che ovviamente non è vero.

Poiché questo programma utilizza i valori predefiniti per RS , in pratica scarterà la prima riga del file di input.

3. Stampa righe in un intervallo

Questa è solo una generalizzazione dell'esempio precedente e non merita molte spiegazioni, se non per dire && è il and logico operatore:

awk 'NR>1 && NR < 4' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team

4. Rimozione delle righe di soli spazi bianchi

awk 'NF' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support
17,05 apr 2019,abhishek,guest

AWK suddivide ogni record in campi, in base al separatore di campo specificato in FS variabile. Il separatore di campo predefinito è uno-o-più-caratteri-spazio-bianco (aka, spazi o tabulazioni). Con queste impostazioni, qualsiasi record contenente almeno un carattere non di spazio vuoto conterrà almeno un campo.

In altre parole, l'unico caso in cui NF è 0 ("false") è quando il record contiene solo spazi. Quindi, quell'unica riga stamperà solo record contenenti almeno un carattere non di spazio.

5. Rimozione di tutte le righe vuote

awk '1' RS='' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support

17,05 apr 2019,abhishek,guest

Questo one-liner si basa su un'oscura regola POSIX che specifica se il RS è impostato su una stringa vuota, "quindi i record sono separati da sequenze costituite da un più una o più righe vuote."

Vale la pena menzionare nella terminologia POSIX, una riga vuota è una riga completamente vuota. Le righe che contengono solo spazi bianchi non contano come "vuote".

6. Estrazione dei campi

Questo è probabilmente uno dei casi d'uso più comuni per AWK:estrarre alcune colonne del file di dati.

awk '{ print $1, $3}' FS=, OFS=, file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
,
,
17,abhishek

Qui, ho impostato in modo esplicito sia i separatori di campo di input che di output sul coma. Quando AWK divide un record in campi, memorizza il contenuto del primo campo in $ 1, il contenuto del secondo campo in $ 2 e così via. Non lo uso qui, ma vale la pena ricordare che $ 0 è l'intero record.

In questa riga, potresti aver notato che uso un blocco azione senza uno schema. In tal caso, viene assunto 1 ("true") per il pattern, quindi il blocco di azioni viene eseguito per ogni record.

A seconda delle tue esigenze, potrebbe non produrre ciò che vorremmo per le righe vuote o di soli spazi bianchi. In tal caso, quella seconda versione potrebbe essere leggermente migliore:

awk 'NF { print $1, $3 }' FS=, OFS=, file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
17,abhishek

In entrambi i casi, ho passato valori personalizzati per FS e OFS sulla riga di comando. Un'altra opzione sarebbe quella di utilizzare uno speciale BEGIN blocco all'interno del programma AWK per inizializzare quelle variabili prima della lettura del primo record. Quindi, a seconda dei tuoi gusti, potresti preferire scrivere che invece:

awk 'BEGIN { FS=OFS="," } NF { print $1, $3 }' file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
17,abhishek

Vale la pena menzionare qui che puoi anche usare END blocchi per eseguire alcune attività dopo la lettura dell'ultimo record. Come lo vedremo solo ora. Detto questo, ammetto che questo è tutt'altro che perfetto poiché le linee di soli spazi bianchi non vengono gestite in modo elegante. Vedremo presto una possibile soluzione, ma prima facciamo un po' di conti...

7. Esecuzione di calcoli per colonna

AWK supporta gli operatori aritmetici standard. E convertirà automaticamente i valori tra testo e numeri a seconda del contesto. Inoltre, puoi utilizzare le tue variabili per memorizzare valori intermedi. Tutto ciò che ti permette di scrivere programmi compatti per eseguire calcoli su colonne di dati:

awk '{ SUM=SUM+$1 } END { print SUM }' FS=, OFS=, file
263

O, equivalentemente usando il += sintassi abbreviata:

awk '{ SUM+=$1 } END { print SUM }' FS=, OFS=, file
263

Si prega di notare che le variabili AWK non devono essere dichiarate prima dell'uso. Si presume che una variabile non definita contenga la stringa vuota. Che, secondo le regole di conversione del tipo AWK, è uguale al numero 0. A causa di questa funzione, non mi sono preoccupato di gestire esplicitamente il caso in cui $1 contiene testo (nell'intestazione), spazi bianchi o semplicemente niente. In tutti questi casi, conterà come 0 e non interferirà con la nostra somma. Naturalmente, sarebbe diverso se eseguissi invece le moltiplicazioni. Quindi, perché non dovresti usare la sezione commenti per suggerire una soluzione per quel caso?

8. Conteggio del numero di righe non vuote

Ho già menzionato il END regola prima. Ecco un'altra possibile applicazione per contare il numero di righe non vuote in un file:

awk '/./ { COUNT+=1 } END { print COUNT }' file
9

Qui ho usato il COUNT variabile e l'ha incrementata (+=1 ) per ogni riga che corrisponde all'espressione regolare /./ . Ovvero ogni riga contenente almeno un carattere. Infine, il blocco END viene utilizzato per visualizzare il risultato finale una volta che l'intero file è stato elaborato. Non c'è niente di speciale nel nome COUNT . Avrei potuto usare Count , count , n , xxxx o qualsiasi altro nome conforme alle regole di denominazione delle variabili AWK

Tuttavia, questo risultato è corretto? Bene, dipende dalla tua definizione di linea "vuota". Se ritieni che solo le righe vuote (secondo POSIX) siano vuote, allora questo è corretto. Ma forse preferiresti considerare vuote anche le righe di soli spazi bianchi?

awk 'NF { COUNT+=1 } END { print COUNT }' file
8

Questa volta il risultato è diverso poiché quella versione successiva ignora anche le righe di soli spazi bianchi, mentre la versione iniziale ignorava solo le righe vuote. Riesci a vedere la differenza? Te lo lascio capire da solo. Non esitare a utilizzare la sezione commenti se questo non è abbastanza chiaro!

Infine, se sei interessato solo alle linee di dati, e dato il mio particolare file di dati di input, potrei invece scriverlo:

awk '+$1 { COUNT+=1 } END { print COUNT }' file
7

Funziona grazie alle regole di conversione del tipo AWK. Il più unario nel pattern forza la valutazione di $1 in un contesto numerico. Nel mio file, i record di dati contengono un numero nel loro primo campo. I record non di dati (intestazione, righe vuote, righe di soli spazi bianchi) contengono testo o nulla. Tutti sono uguali a 0 quando vengono convertiti in numeri.

Nota con l'ultima soluzione, anche un record per un utente con 0 crediti verrebbe scartato.

B. Utilizzo di array in AWK

Gli array sono una potente funzionalità di AWK. Tutti gli array in AWK sono array associativi, quindi consentono di associare una stringa arbitraria con un altro valore. Se hai familiarità con altri linguaggi di programmazione, potresti conoscerli come hash , tabelle associative , dizionari o mappe .

9. Un semplice esempio di array AWK

Immaginiamo che io voglia conoscere il credito totale per tutti gli utenti. Posso memorizzare una voce per ogni utente in un array associativo e ogni volta che incontro un record per quell'utente, incremento il valore corrispondente memorizzato nell'array.

awk '+$1 { CREDITS[$3]+=$1 }
     END { for (NAME in CREDITS) print NAME, CREDITS[NAME] }' FS=, file
abhishek 17
sonia 129
öle 8
sylvain 109

Ammetto che questo non è più un one-liner. Principalmente a causa del for ciclo utilizzato per visualizzare il contenuto dell'array dopo che il file è stato elaborato. Quindi, torniamo ora agli esempi più brevi:

10. Identificazione di righe duplicate utilizzando AWK

Gli array, proprio come altre variabili AWK, possono essere utilizzati sia nei blocchi di azione che nei pattern. Approfittando di ciò, possiamo scrivere una riga per stampare solo righe duplicate:

awk 'a[$0]++' file
52,01    dec   2018,sonia,team

Il ++ operator è l'operatore di post-incremento ereditato dalla famiglia di linguaggi C (di cui AWK è un membro orgoglioso, grazie a Brian Kernighan è stato uno dei suoi autori originali).

Come suggerisce il nome, l'operatore di post-incremento incrementa ("aggiungi 1") una variabile, ma solo dopo che il suo valore è stato preso per la valutazione dell'espressione inglobante.

In tal caso, a[$0] viene valutato per vedere se il record verrà stampato o meno e, una volta presa la decisione, in tutti i casi, la voce dell'array viene incrementata.

Quindi la prima volta che viene letto un record, a[$0] è indefinito e quindi equivalente a zero per AWK. In modo che il primo record non venga scritto sull'output. Quindi quella voce viene modificata da zero a uno.
La seconda volta che viene letto lo stesso record di input, a[$0] ora è 1. Questo è "vero". La riga verrà stampata. Tuttavia, prima, la voce dell'array viene aggiornata da 1 a 2. E così via.

11. Rimozione di righe duplicate

Come corollario del precedente one-liner, potremmo voler rimuovere le righe duplicate:

awk '!a[$0]++' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support


17,05 apr 2019,abhishek,guest

L'unica differenza è l'uso dell'operatore logico, non (! ) che invertono il valore di verità dell'espressione. Ciò che era falso diventa vero e ciò che era vero diventa falso. La logica non ha assolutamente alcuna influenza sul ++ post incremento che funziona esattamente come prima.

C. Magia del separatore di campi e record

12. Modifica dei separatori di campo

awk '$1=$1' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support

17;05 apr 2019;abhishek;guest

Quel programma imposta il FS e OFS variabile per utilizzare una virgola come separatore del campo di input e un punto e virgola come separatore del campo di output. Poiché AWK non modifica il record di output finché non hai modificato un campo, il $1=$1 il trucco viene utilizzato per forzare AWK a rompere il record e rimontarlo utilizzando il separatore del campo di output.

Ricorda qui che il blocco di azione predefinito è { print } . Quindi potresti riscriverlo in modo più esplicito come:

awk '$1=$1 { print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support

17;05 apr 2019;abhishek;guest

Potresti aver notato che entrambi questi esempi stanno rimuovendo anche le righe vuote. Come mai? Bene, ricorda le regole di conversione AWK:una stringa vuota è "falsa". Tutte le altre stringhe sono "vere". L'espressione $1=$1 è un'affettazione che altera $1 . Tuttavia, anche questa è un'espressione. E restituisce il valore di $1 –che è "falso" per la stringa vuota. Se vuoi davvero tutte le righe, potresti dover scrivere qualcosa del genere invece:

awk '($1=$1) || 1 { print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support



17;05 apr 2019;abhishek;guest

Ricordi il && operatore? Era l'AND logico. || è l'OR logico. La parentesi è necessaria qui a causa delle regole di precedenza degli operatori. Senza di loro, il modello sarebbe stato erroneamente interpretato come $1=($1 || 1) invece. Ti lascio come esercizio per testare come il risultato sarebbe stato diverso allora.

Infine, se non sei troppo appassionato di aritmetica, scommetto che preferirai quella soluzione più semplice:

awk '{ $1=$1; print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support



17;05 apr 2019;abhishek;guest

13. Rimozione di più spazi

awk '$1=$1' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest

Questo è quasi lo stesso programma del precedente. Tuttavia, ho lasciato i separatori di campo sui valori predefiniti. Quindi, più spazi bianchi vengono utilizzati come separatore del campo di input, ma solo uno spazio viene utilizzato come separatore del campo di output. Questo ha il piacevole effetto collaterale di unire multipli spazi bianchi in uno spazio.

14. Unire le linee usando AWK

Abbiamo già usato OFS , il separatore del campo di output. Come avrai intuito, ha il ORS controparte per specificare il separatore del record di output:

awk '{ print $3 }' FS=, ORS=' ' file; echo
USER sylvain sonia sonia sonia sylvain öle    abhishek

Qui, ho usato uno spazio dopo ogni record invece di un carattere di nuova riga. Questo one-liner è sufficiente in alcuni casi d'uso, ma presenta comunque alcuni inconvenienti.

Ovviamente, non scarta le righe di soli spazi bianchi (gli spazi extra dopo öle provengono da quello). Quindi, potrei finire per usare invece una semplice espressione regolare:

awk '/[^[:space:]]/ { print $3 }' FS=, ORS=' ' file; echo
USER sylvain sonia sonia sonia sylvain öle abhishek

Ora va meglio, ma c'è ancora un possibile problema. Sarà più ovvio se cambiamo il separatore in qualcosa di visibile:

awk '/[^[:space:]]/ { print $3 }' FS=, ORS='+' file; echo
USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek+

C'è un separatore aggiuntivo alla fine della riga, perché il separatore di campo è scritto dopo ogni record. Compreso l'ultimo.

Per risolvere il problema, riscriverò il programma per visualizzare un separatore personalizzato prima il record, a partire dal secondo record di output.

awk '/[^[:space:]]/ { print SEP $3; SEP="+" }' FS=, ORS='' file; echo
USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek

Poiché mi occupo di aggiungere il separatore da solo, ho anche impostato il separatore di record di output AWK standard sulla stringa vuota. Tuttavia, quando inizi a occuparti di separatori o formattazione, potrebbe essere il segno che dovresti considerare di utilizzare il printf funzione invece di print dichiarazione. Come lo vedremo adesso.

D. Formattazione del campo

Ho già accennato alla relazione tra i linguaggi di programmazione AWK e C. Tra le altre cose, dalla libreria standard del linguaggio C AWK eredita il potente printf funzione, consentendo un grande controllo sulla formattazione del testo inviato all'output.

Il printf La funzione accetta un formato come primo argomento, contenente sia il testo normale che verrà restituito alla lettera sia i caratteri jolly utilizzati per formattare diverse sezioni dell'output. I caratteri jolly sono identificati dal % carattere. Il più comune è %s (per la formattazione delle stringhe), %d (per la formattazione dei numeri interi) e %f (per la formattazione dei numeri in virgola mobile). Poiché questo può essere piuttosto astratto, vediamo un esempio:

awk '+$1 { printf("%s ",  $3) }' FS=, file; echo
sylvain sonia sonia sonia sylvain öle abhishek

Potresti notare, come l'opposto di print istruzione, il printf la funzione non utilizza il OFS e ORS valori. Quindi, se vuoi un separatore, devi menzionarlo esplicitamente come ho fatto io aggiungendo uno spazio alla fine della stringa di formato. Questo è il prezzo da pagare per avere il pieno controllo dell'output.

Sebbene non sia affatto un identificatore di formato, questa è un'ottima occasione per introdurre il \n notazione che può essere utilizzata in qualsiasi stringa AWK per rappresentare un carattere di nuova riga.

awk '+$1 { printf("%s\n",  $3) }' FS=, file
sylvain
sonia
sonia
sonia
sylvain
öle
abhishek

15. Produzione di risultati tabulari

AWK applica un formato dati record/campo basato su delimitatori. Tuttavia, utilizzando il printf funzione, puoi anche produrre output tabulare a larghezza fissa. Perché ogni identificatore di formato in un printf l'istruzione può accettare un parametro di larghezza opzionale:

awk '+$1 { printf("%10s | %4d\n",  $3, $1) }' FS=, file
   sylvain |   99
     sonia |   52
     sonia |   52
     sonia |   25
   sylvain |   10
       öle |    8
  abhishek |   17

Come puoi vedere, specificando la larghezza di ciascun campo, AWK li riempie a sinistra con spazi. Per il testo, di solito è preferibile riempire a destra, cosa che può essere ottenuta utilizzando un numero di larghezza negativo. Inoltre, per gli interi, potremmo voler riempire i campi con zeri anziché spazi. Questo può essere ottenuto utilizzando uno 0 esplicito prima della larghezza del campo:

awk '+$1 { printf("%-10s | %04d\n",  $3, $1) }' FS=, file
sylvain    | 0099
sonia      | 0052
sonia      | 0052
sonia      | 0025
sylvain    | 0010
öle        | 0008
abhishek   | 0017

16. Gestire i numeri in virgola mobile

Il %f il formato non merita molte spiegazioni...

awk '+$1 { SUM+=$1; NUM+=1 } END { printf("AVG=%f",SUM/NUM); }' FS=, file
AVG=37.571429

… tranne forse per dire che quasi sempre vuoi impostare in modo esplicito la larghezza del campo e la precisione del risultato visualizzato:

awk '+$1 { SUM+=$1; NUM+=1 } END { printf("AVG=%6.1f",SUM/NUM); }' FS=, file
AVG=  37.6

Qui, la larghezza del campo è 6, il che significa che il campo occuperà lo spazio di 6 caratteri (compreso il punto, ed eventualmente riempito con spazi a sinistra come al solito). La precisione .1 significa che vogliamo visualizzare il numero con 1 decimale dopo il punto. Ti faccio indovinare cosa %06.1 verrebbe visualizzato invece.

E. Utilizzo delle funzioni di stringa in AWK

Oltre a printf funzione, AWK contiene poche altre belle funzioni di manipolazione delle stringhe. In quel dominio, le moderne implementazioni come Gawk hanno un insieme più ricco di funzioni interne al prezzo di una minore portabilità. Per quanto mi riguarda, rimarrò qui con solo alcune funzioni definite da POSIX che dovrebbero funzionare allo stesso modo ovunque.

17. Conversione del testo in maiuscolo

Questo lo uso molto, perché gestisce bene i problemi di internazionalizzazione:

awk '$3 { print toupper($0); }' file
99,01 JUN 2018,SYLVAIN,TEAM:::ADMIN
52,01    DEC   2018,SONIA,TEAM
52,01    DEC   2018,SONIA,TEAM
25,01    JAN   2019,SONIA,TEAM
10,01 JAN 2019,SYLVAIN,TEAM:::ADMIN
8,12    JUN   2018,ÖLE,TEAM:SUPPORT
17,05 APR 2019,ABHISHEK,GUEST

In effetti, questa è probabilmente la soluzione migliore e più portatile per convertire il testo in maiuscolo dalla shell.

18. Modifica di parte di una stringa

Usando il substr comando, puoi dividere una stringa di caratteri a una determinata lunghezza. Qui lo uso per mettere in maiuscolo solo il primo carattere del terzo campo:

awk '{ $3 = toupper(substr($3,1,1)) substr($3,2) } $3' FS=, OFS=, file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,Sylvain,team:::admin
52,01    dec   2018,Sonia,team
52,01    dec   2018,Sonia,team
25,01    jan   2019,Sonia,team
10,01 jan 2019,Sylvain,team:::admin
8,12    jun   2018,Öle,team:support
17,05 apr 2019,Abhishek,guest

Il substr La funzione prende la stringa iniziale, l'indice (a base 1) del primo carattere da estrarre e il numero di caratteri da estrarre. Se manca l'ultimo argomento, substr prende tutti i restanti caratteri della stringa.

Quindi, substr($3,1,1) restituirà il primo carattere di $3 e substr($3,2) ai restanti.

19. Divisione dei campi in sottocampi

Il modello di dati del campo di registrazione AWK è davvero carino. Tuttavia, a volte vuoi dividere i campi stessi in più parti in base a un separatore interno:

awk '+$1 { split($2, DATE, " "); print $1,$3, DATE[2], DATE[3] }' FS=, OFS=, file
99,sylvain,jun,2018
52,sonia,dec,2018
52,sonia,dec,2018
25,sonia,jan,2019
10,sylvain,jan,2019
8,öle,jun,2018
17,abhishek,apr,2019

Sorprendentemente, funziona anche se alcuni dei miei campi sono separati da più di uno spazio bianco. Principalmente per ragioni storiche, quando il separatore è un singolo spazio, split considererà "gli elementi sono separati da sequenze di spazi bianchi". E non solo da uno solo. Il FS la variabile speciale segue la stessa convenzione.

Tuttavia, nel caso generale, una stringa di caratteri corrisponde a un carattere. Quindi, se hai bisogno di qualcosa di più complesso, devi ricordare che il separatore di campo è un'espressione regolare estesa.

A titolo di esempio, vediamo come verrebbe gestito il campo gruppo che sembra essere un campo multivalore utilizzando i due punti come separatore:

awk '+$1 { split($4, GRP, ":"); print $3, GRP[1], GRP[2] }' FS=, file
sylvain team
sonia team
sonia team
sonia team
sylvain team
öle team support
abhishek guest

Mentre mi sarei aspettato di visualizzare fino a due gruppi per utente, ne mostra solo uno per la maggior parte di essi. Questo problema è causato dalle più occorrenze del separatore. Quindi, la soluzione è:

awk '+$1 { split($4, GRP, /:+/); print $3, GRP[1], GRP[2] }' FS=, file
sylvain team admin
sonia team
sonia team
sonia team
sylvain team admin
öle team support
abhishek guest

Le barre al posto delle virgolette indicano che il letterale è un'espressione regolare anziché una stringa semplice e il segno più indica che questa espressione corrisponderà a una o più occorrenze del carattere precedente. Quindi, in tal caso, ogni separatore è composto (dalla sequenza più lunga di) uno o più due punti consecutivi.

20. Ricerca e sostituzione con comandi AWK

Parlando di espressioni regolari, a volte vuoi eseguire sostituzioni come sed s///g comando, ma solo su un campo. Il gsub comando è ciò di cui hai bisogno in questo caso:

awk '+$1 { gsub(/ +/, "-", $2); print }' FS=, file
99 01-jun-2018 sylvain team:::admin
52 01-dec-2018 sonia team
52 01-dec-2018 sonia team
25 01-jan-2019 sonia team
10 01-jan-2019 sylvain team:::admin
8 12-jun-2018 öle team:support
17 05-apr-2019 abhishek guest

Il gsub La funzione accetta un'espressione regolare da cercare, una stringa sostitutiva e la variabile contenente il testo da modificare. Se manca quello successivo, si presume $ 0.

F. Lavorare con comandi esterni in AWK

Un'altra grande caratteristica di AWK è che puoi facilmente invocare comandi esterni per elaborare i tuoi dati. Ci sono fondamentalmente due modi per farlo:usando il system istruzione per richiamare un programma e lasciare che mescoli il suo output nel flusso di output AWK. Oppure utilizzando una pipe in modo che AWK possa acquisire l'output del programma esterno per un controllo più preciso del risultato.

Questi possono essere argomenti enormi di per sé, ma ecco alcuni semplici esempi per mostrarti il ​​potere dietro queste funzionalità.

21. Aggiunta della data sopra un file

awk 'BEGIN { printf("UPDATED: "); system("date") } /^UPDATED:/ { next } 1' file
UPDATED: Thu Feb 15 00:31:03 CET 2018
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

In quel programma AWK, inizio visualizzando il lavoro AGGIORNATO. Quindi il programma richiama la date esterna comando, che invierà il suo risultato sull'output subito dopo il testo prodotto da AWK in quella fase.
Il resto del programma AWK rimuove semplicemente una dichiarazione di aggiornamento eventualmente presente nel file e stampa tutte le altre righe (con la regola 1 ).

Nota il next dichiarazione. Viene utilizzato per interrompere l'elaborazione del record corrente. It is a standard way of ignoring some records from the input file.

22. Modifying a field externally

For more complex cases, you may need to consider the | getline VARIABLE idiom of AWK:

awk '+$1 { CMD | getline $5; close(CMD); print }' CMD="uuid -v4" FS=, OFS=, file
99,01 jun 2018,sylvain,team:::admin,5e5a1bb5-8a47-48ee-b373-16dc8975f725
52,01    dec   2018,sonia,team,2b87e9b9-3e75-4888-bdb8-26a9b34facf3
52,01    dec   2018,sonia,team,a5fc22b5-5388-49be-ac7b-78063cbbe652
25,01    jan   2019,sonia,team,3abb0432-65ef-4916-9702-a6095f3fafe4
10,01 jan 2019,sylvain,team:::admin,592e9e80-b86a-4833-9e58-1fe2428aa2a2
8,12    jun   2018,öle,team:support,3290bdef-fd84-4026-a02c-46338afd4243
17,05 apr 2019,abhishek,guest,e213d756-ac7f-4228-818f-1125cba0810f

This will run the command stored in the CMD variable, read the first line of the output of that command, and store it into the variable $5 .

Pay special attention to the close statement, crucial here as we want AWK to create a new instance of the external command each time it executes the CMD | getline statement. Without the close statement, AWK would instead try to read several lines of output from the same command instance.

23. Invoking dynamically generated commands

Commands in AWK are just plain strings without anything special. It is the pipe operator that triggers external programs execution. So, if you need, you can dynamically construct arbitrary complex commands by using the AWK string manipulation functions and operators.

awk '+$1 { cmd = sprintf(FMT, $2); cmd | getline $2; close(cmd); print }' FMT='date -I -d "%s"'  FS=, file
99 2018-06-01 sylvain team:::admin
52 2018-12-01 sonia team
52 2018-12-01 sonia team
25 2019-01-01 sonia team
10 2019-01-01 sylvain team:::admin
8 2018-06-12 öle team:support
17 2019-04-05 abhishek guest

We have already met the printf function. sprintf is very similar but will return the built string rather than sending it to the output.

24. Joining data

To show you the purpose of the close statement, I let you try out that last example:

awk '+$1 { CMD | getline $5; print }' CMD='od -vAn -w4 -t x /dev/urandom' FS=, file
99 01 jun 2018 sylvain team:::admin  1e2a4f52
52 01    dec   2018 sonia team  c23d4b65
52 01    dec   2018 sonia team  347489e5
25 01    jan   2019 sonia team  ba985e55
10 01 jan 2019 sylvain team:::admin  81e9a01c
8 12    jun   2018 öle team:support  4535ba30
17 05 apr 2019 abhishek guest  80a60ec8

As the opposite of the example using the uuid command above, there is here only one instance of od launched while the AWK program is running, and when processing each record, we read one more line of the output of that same process.

Conclusion

That quick tour of AWK certainly can’t replace a full-fledged course or tutorial on that tool. However, for those of you that weren’t familiar with it, I hope it gave you enough ideas so you can immediately add AWK to your toolbox.

On the other hand, if you were already an AWK aficionado, you might have found here some tricks you can use to be more efficient or simply to impress your friends.

However, I do not pretend been exhaustive. So, in all cases, don’t hesitate to share your favorite AWK one-liner or any other AWK tips using the comment section below!


Linux
  1. Iniziare con Zsh

  2. Iniziare con il comando tac di Linux

  3. Iniziare con il comando cat di Linux

  4. Iniziare con PostgreSQL su Linux

  5. Guida introduttiva a Multipass – Esecuzione di macchine virtuali Ubuntu

Guida introduttiva agli esempi di comandi di Alpine Linux Apk

Guida introduttiva a Nano Text Editor [Guida per principianti]

Guida introduttiva a Markdown [Guida per principianti]

Guida introduttiva al comando SED [Guida per principianti]

Guida introduttiva a Tmux [Guida per principianti]

Guida introduttiva al comando Tar