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.