Passa la variabile shell come / pattern / a awk


59

Avere quanto segue in una delle mie funzioni di shell:

function _process () {
  awk -v l="$line" '
  BEGIN {p=0}
  /'"$1"'/ {p=1}
  END{ if(p) print l >> "outfile.txt" }
  '
}

, quindi quando viene chiamato come _process $arg, $argviene passato come $1e utilizzato come modello di ricerca. Funziona in questo modo, perché la shell si espande $1al posto del modello awk! Inoltre lpuò essere utilizzato all'interno del programma awk, essendo dichiarato con -v l="$line". Tutto bene.

È possibile allo stesso modo dare uno schema per la ricerca come variabile?

Il seguito non funzionerà,

awk -v l="$line" -v search="$pattern" '
  BEGIN {p=0}
  /search/ {p=1}
  END{ if(p) print l >> "outfile.txt" }
  '

, poiché awk non interpreterà /search/come una variabile, ma invece letteralmente.

Risposte:


46

Usa l' ~operatore di awk e non è necessario fornire una regex letterale sul lato destro:

function _process () {
    awk -v l="$line" -v pattern="$1" '
        $0 ~ pattern {p=1} 
        END {if(p) print l >> "outfile.txt"}
    '  
}

Anche se questo sarebbe più efficiente (non è necessario leggere l'intero file)

function _process () {
    grep -q "$1" && echo "$line"
}

A seconda del modello, potrebbe essere necessario grep -Eq "$1"


Questo è esattamente ciò che risolve questo nel modo che desideravo (primo esempio), perché mantiene la semantica, che era il mio obiettivo. Grazie.
Branquito,

1
Non ho notato la rimozione del blocco BEGIN: una variabile non assegnata viene trattata come 0 in un contesto numerico o in caso contrario la stringa vuota. Quindi, una variabile non assegnata sarà falsa inif (p) ...
glenn jackman il

sì, ho notato, deve essere impostato su zero sul blocco BEGIN ogni volta, poiché funge da interruttore. Ma è interessante notare che ora ho provato a utilizzare lo script $0 ~ patterne non funziona, tuttavia con /'"$1"'/esso funziona !? : O
branquito,

forse ha qualcosa a che fare con il modo in cui $lineviene recuperato, la ricerca di pattern viene eseguita sull'output di whois $line, $lineproveniente da un file in un blocco WHILE DO.
Branquito,

Si prega di mostrare il contenuto di $line- farlo nella domanda per una corretta formattazione.
Glenn Jackman,

17
awk  -v pattern="$1" '$0 ~ pattern'

Ha un problema in quanto awkespande le sequenze di escape ANSI C (come \nper newline, \fper feed di moduli, \\per barra rovesciata e così via) in $1. Quindi diventa un problema se $1contiene caratteri di backslash che sono comuni nelle espressioni regolari (con GNU awk4.2 o successivo, anche i valori che iniziano @/e finiscono in) /sono un problema ). Un altro approccio che non soffre di questo problema è quello di scriverlo:

PATTERN=$1 awk '$0 ~ ENVIRON["PATTERN"]'

Quanto sarà grave dipenderà awkdall'implementazione.

$ nawk -v 'a=\.' 'BEGIN {print a}'
.
$ mawk -v 'a=\.' 'BEGIN {print a}'
\.
$ gawk -v 'a=\.' 'BEGIN {print a}'
gawk: warning: escape sequence `\.' treated as plain `.'
.
$ gawk5.0.1 -v 'a=@/foo/' BEGIN {print a}'
foo

Tutti awkfunzionano allo stesso modo per sequenze di escape valide:

$ a='\\-\b' awk 'BEGIN {print ENVIRON["a"]}' | od -tc
0000000   \   \   -   \   b  \n
0000006

(contenuto di $apassato così com'è)

$ awk -v a='\\-\b' 'BEGIN {print a}' | od -tc
0000000   \   -  \b  \n
0000004

( \\modificato in \e \bmodificato in un carattere backspace).


Quindi stai dicendo che se il modello \d{3}dovesse trovare, ad esempio, tre cifre, non funzionerebbe come previsto, se ti capissi bene?
Branquito,

2
per \dcui non è una sequenza di escape C valida, che dipende dalla tua awkimplementazione (esegui awk -v 'a=\d{3}' 'BEGIN{print a}'per verificare). Ma per \` or \ b , yes definitely. (BTW, I don't know of any awk implementations that understands \ d` significa una cifra).
Stéphane Chazelas,

dice: avviso awk - sequenza di escape \d' treated as plain d 'd {3}, quindi suppongo che avrei un problema in questo caso?
Branquito,

1
Scusa, mia cattiva, ho avuto un refuso nella mia risposta. Il nome della variabile d'ambiente deve corrispondere ENVIRON["PATTERN"]per la PATTERNvariabile d'ambiente. Se vuoi usare una variabile di shell, devi prima esportarla ( export variable) o usare la ENV=VALUE awk '...ENVIRON["ENV"]'sintassi di passaggio env-var come nella mia risposta.
Stéphane Chazelas,

1
Perché è necessario esportare una variabile di shell affinché venga passata nell'ambiente a un comando.
Stéphane Chazelas,

5

Prova qualcosa del tipo:

awk -v l="$line" -v search="$pattern" 'BEGIN {p=0}; { if ( match( $0, search )) {p=1}}; END{ if(p) print l >> "outfile.txt" }'

Se questo si comporta come /regex/in termini di ricerca del modello, questa potrebbe essere una buona soluzione. Cercherò.
Branquito,

1
I test rapidi che ho eseguito sembrano funzionare allo stesso modo, ma non comincerò nemmeno a garantirlo ... :)
Hunter Eidson,

0

No, ma puoi semplicemente interpolare il pattern nella stringa tra virgolette che passi a awk:

awk -v l="$line" "BEGIN {p=0}; /$pattern/ {p=1}; END{ if(p) print l >> \"outfile.txt\" }"

Nota che ora devi sfuggire al letterale awk tra virgolette doppie, ma è ancora il modo più semplice per farlo.


In questo modo è sicuro se $patterncontiene spazi, il mio esempio dall'alto funzionerà poiché $ 1 è protetto con virgolette doppie "$ 1", tuttavia non è sicuro cosa succede nel tuo caso.
Branquito,

2
L'esempio originale termina la stringa a virgoletta singola al secondo ', quindi protegge le $1virgolette doppie e quindi applica un'altra stringa a virgoletta singola per la seconda metà del programma awk. Se ho capito bene, questo dovrebbe avere esattamente lo stesso effetto di proteggere le $1virgolette singole esterne - awk non vede mai le doppie virgolette che ci metti intorno.
Kilian Foth,

4
Ma se $patterncontiene ^/ {system("rm -rf /")};, allora sei nei guai.
Stéphane Chazelas,

è solo questo aspetto negativo di questo approccio, avendo tutto racchiuso in ""?
Branquito,

-3

È possibile utilizzare la funzione eval che risolve in questo esempio la variabile nets prima dell'esecuzione di awk.

nets="searchtext"
eval "awk '/"${nets}"/'" file.txt
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.