Come posso usare le variabili shell in uno script awk?


290

Ho trovato alcuni modi per passare variabili di shell esterne a uno awkscript, ma sono confuso su 'e ".

Innanzitutto, ho provato con uno script di shell:

$ v=123test
$ echo $v
123test
$ echo "$v"
123test

Quindi ho provato awk:

$ awk 'BEGIN{print "'$v'"}'
$ 123test
$ awk 'BEGIN{print '"$v"'}'
$ 123

Perché la differenza

Infine ho provato questo:

$ awk 'BEGIN{print " '$v' "}'
$  123test
$ awk 'BEGIN{print ' "$v" '}'
awk: cmd. line:1: BEGIN{print
awk: cmd. line:1:             ^ unexpected newline or end of string 

Sono confuso su questo.


2
Mi piace -v come mostrato di seguito, ma questo è davvero un ottimo esercizio nel pensare a come proteggere le cose dal guscio. In questo modo, il mio primo taglio usa barre rovesciate su spazi e segni del dollaro. Inutile dire che gli esempi qui valgono la pena.
Chris,


Se la tua ricerca di awk richiede un'espressione regolare , non puoi metterla /var/. Invece, usa tilde:awk -v var="$var" '$0 ~ var'
Noam Manos

Risposte:


497

Inserimento di variabili shell awk

può essere fatto in diversi modi. Alcuni sono migliori di altri. Questo dovrebbe riguardare la maggior parte di essi. Se hai un commento, ti preghiamo di lasciare qui sotto. v1.5


Utilizzo -v (il modo migliore, il più portatile)

Usa l' -vopzione: (PS usa uno spazio dopo -vo sarà meno portatile. Ad esempio, awk -v var=no awk -vvar=)

variable="line one\nline two"
awk -v var="$variable" 'BEGIN {print var}'
line one
line two

Questo dovrebbe essere compatibile con la maggior parte awke la variabile è disponibile anche nel BEGINblocco:

Se hai più variabili:

awk -v a="$var1" -v b="$var2" 'BEGIN {print a,b}'

Attenzione . Come scrive Ed Morton, le sequenze di escape verranno interpretate in modo da \tdiventare reali tabe non \tse questo è ciò che cerchi. Può essere risolto utilizzando ENVIRON[]o accedendo tramiteARGV[]

PS Se ti piacciono tre barre verticali come separatore |||, non può essere sfuggito, quindi usa-F"[|][|][|]"

Esempio su come ottenere dati da una locanda di programma / funzione awk(qui viene utilizzata la data)

awk -v time="$(date +"%F %H:%M" -d '-1 minute')" 'BEGIN {print time}'

Variabile dopo il blocco di codice

Qui otteniamo la variabile dopo il awkcodice. Funzionerà perfettamente finché non avrai bisogno della variabile nel BEGINblocco:

variable="line one\nline two"
echo "input data" | awk '{print var}' var="${variable}"
or
awk '{print var}' var="${variable}" file
  • Aggiunta di più variabili:

awk '{print a,b,$0}' a="$var1" b="$var2" file

  • In questo modo possiamo anche impostare diversi Field Separator FSper ogni file.

awk 'some code' FS=',' file1.txt FS=';' file2.ext

  • La variabile dopo il blocco di codice non funzionerà per il BEGINblocco:

echo "input data" | awk 'BEGIN {print var}' var="${variable}"


Qui-string

La variabile può anche essere aggiunta awkall'utilizzo di una stringa qui dalle shell che le supportano (incluso Bash):

awk '{print $0}' <<< "$variable"
test

Questo è lo stesso di:

printf '%s' "$variable" | awk '{print $0}'

PS questo tratta la variabile come input di file.


ENVIRON ingresso

Come scrive TrueY, è possibile utilizzare ENVIRONper stampare le variabili di ambiente . Impostando una variabile prima di eseguire AWK, puoi stamparla in questo modo:

X=MyVar
awk 'BEGIN{print ENVIRON["X"],ENVIRON["SHELL"]}'
MyVar /bin/bash

ARGV ingresso

Come scrive Steven Penny, puoi usare ARGVper ottenere i dati in awk:

v="my data"
awk 'BEGIN {print ARGV[1]}' "$v"
my data

Per ottenere i dati nel codice stesso, non solo BEGIN:

v="my data"
echo "test" | awk 'BEGIN{var=ARGV[1];ARGV[1]=""} {print var, $0}' "$v"
my data test

Variabile all'interno del codice: UTILIZZARE CON ATTENZIONE

Puoi usare una variabile all'interno del awkcodice, ma è disordinata e difficile da leggere e, come Charles Duffysottolinea, questa versione potrebbe anche essere vittima dell'iniezione di codice. Se qualcuno aggiunge elementi non validi alla variabile, verrà eseguita come parte del awkcodice.

Funziona estraendo la variabile all'interno del codice, quindi diventa parte di essa.

Se si desidera apportare awkmodifiche che cambiano in modo dinamico con l'uso di variabili, è possibile farlo in questo modo, ma NON utilizzarlo per variabili normali.

variable="line one\nline two"
awk 'BEGIN {print "'"$variable"'"}'
line one
line two

Ecco un esempio di iniezione di codice:

variable='line one\nline two" ; for (i=1;i<=1000;++i) print i"'
awk 'BEGIN {print "'"$variable"'"}'
line one
line two
1
2
3
.
.
1000

È possibile aggiungere molti comandi in awkquesto modo. Perfino farlo andare in crash con comandi non validi.


Informazioni extra:

Uso della doppia citazione

È sempre bene raddoppiare la variabile tra virgolette. In "$variable"
caso contrario, verranno aggiunte più righe come un'unica riga lunga.

Esempio:

var="Line one
This is line two"

echo $var
Line one This is line two

echo "$var"
Line one
This is line two

Altri errori che puoi ottenere senza virgolette doppie:

variable="line one\nline two"
awk -v var=$variable 'BEGIN {print var}'
awk: cmd. line:1: one\nline
awk: cmd. line:1:    ^ backslash not last character on line
awk: cmd. line:1: one\nline
awk: cmd. line:1:    ^ syntax error

E con virgoletta singola, non espande il valore della variabile:

awk -v var='$variable' 'BEGIN {print var}'
$variable

Ulteriori informazioni su AWK e variabili

Leggi questa domanda .


2
"disordinato e difficile da leggere" ignora la più importante preoccupazione di sicurezza dell'iniezione di codice quando si sostituiscono direttamente le stringhe in codice awk.
Charles Duffy,

leggendo la risposta sopra posso eseguire il mio script senza errori ma non fa il lavoro: awk -v repo = "$ 1" -v tag = "$ 2" '{sub (/ image: registerabx.azurecr.io \ / { print repo}: ([a-z0-9] +) $ /, "immagine: registerabc.azurecr. io / {print repo}: {print tag}");} 1 './services/appscompose.yaml >> newcompose.yaml. È a causa della parentesi nidificata {?
Darion Badlydone,

@DarionBadlydone Prova questo awk -v repo="$1" -v tag="$2" 'BEGIN {print "repo="repo,"tag="tag}'. Vedrà se stampa la variabile. Pubblica una domanda se non riesci a capire.
Jotne,

@Jotne sì, stampa i valori così ho provato in questo modo: awk -v repo = "$ 1" -v tag = "$ 2" '{print "{sub (/ image: registerabc.azurecr.io/"repo" :( [a-z0-9] +) $ /, \ "image: registerabc.azurecr.io/"repo":"tag"\");}1"} './services/appscompose.yaml >> newcompose.yaml ma non funziona come previsto. Sostituisce ogni riga del file sorgente con il comando stampato
Darion Badlydone,

@Jotne L'ho fatto con sed, grazie comunque
Darion Badlydone,

28

Sembra che il buon vecchio ENVIRON l'hash incorporato non è menzionato affatto. Un esempio del suo utilizzo:

$ X=Solaris awk 'BEGIN{print ENVIRON["X"], ENVIRON["TERM"]}'
Solaris rxvt

4
Questo è un buon suggerimento perché trasmette i dati alla lettera. -vnon funziona quando il valore contiene barre rovesciate.
quell'altro ragazzo il

2
@thatotherguy Non lo sapevo! Ho pensato che se lo uso awk -v x='\c\d' ..., verrà utilizzato correttamente. Ma quando xviene stampato awk rilascia il famoso awk: warning: escape sequence '\c' treated as plain 'c'messaggio di errore ... Grazie!
Vero

Funziona correttamente - correttamente in questo contesto significa espandere le sequenze di escape perché è così che è -vstato progettato per funzionare in modo da poterlo utilizzare \tnella variabile e far corrispondere una scheda letterale nei dati, ad esempio. Se questo non è il comportamento che desideri, allora non usi il -vtuo uso ARGV[]o ENVIRON[].
Ed Morton,

9

Utilizzare uno di questi a seconda della modalità di gestione delle barre rovesciate nelle variabili shell ( avarè una variabile awk, svarè una variabile shell):

awk -v avar="$svar" '... avar ...' file
awk 'BEGIN{avar=ARGV[1];ARGV[1]=""}... avar ...' "$svar" file

Vedi http://cfajohnson.com/shell/cus-faq-2.html#Q24 per dettagli e altre opzioni. Il primo metodo sopra è quasi sempre l'opzione migliore e ha la semantica più ovvia.


6

È possibile passare l' opzione della riga di comando -v con un nome di variabile ( v) e un valore ( =) della variabile di ambiente ( "${v}"):

% awk -vv="${v}" 'BEGIN { print v }'
123test

O per renderlo più chiaro (con molti meno vs):

% environment_variable=123test
% awk -vawk_variable="${environment_variable}" 'BEGIN { print awk_variable }'
123test

3

Puoi utilizzare ARGV:

v=123test
awk 'BEGIN {print ARGV[1]}' "$v"

Nota che se vuoi continuare nel corpo, dovrai regolare l'ARGC:

awk 'BEGIN {ARGC--} {print ARGV[2], $0}' file "$v"

1

Ho appena cambiato la risposta di @ Jotne per "for loop".

for i in `seq 11 20`; do host myserver-$i | awk -v i="$i" '{print "myserver-"i" " $4}'; done

1
Questo sembra semplicemente essere un altro esempio di come usare l' -vopzione di Awk che è già stata menzionata in molte delle risposte esistenti. Se vuoi mostrare come eseguire Awk in un ciclo, questa è davvero una domanda diversa.
Tripleee

0

Ho dovuto inserire la data all'inizio delle righe di un file di registro ed è fatto come di seguito:

DATE=$(date +"%Y-%m-%d")
awk '{ print "'"$DATE"'", $0; }' /path_to_log_file/log_file.log

Può essere reindirizzato a un altro file per salvare


La doppia citazione - singola citazione - doppia citazione era esattamente ciò di cui avevo bisogno per far funzionare la mia.
user53029

2
Ciò è già stato menzionato nella risposta accettata come metodo da non utilizzare a causa delle vulnerabilità legate all'iniezione di codice. Quindi le informazioni qui sono ridondanti (già descritte nella risposta accettata) e incomplete (non menzionano i problemi con questo metodo).
Jason S,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.