Passa a variável do shell como / pattern / to awk

53

Tendo o seguinte em uma das minhas funções do shell:

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

, quando chamado como _process $arg , $arg é passado como $1 e usado como um padrão de pesquisa. Funciona dessa maneira, porque o shell expande $1 no lugar do padrão awk! Também l pode ser usado dentro do programa awk, sendo declarado com -v l="$line" . Tudo bem.

É possível da mesma maneira dar um padrão para pesquisar como uma variável?

A seguir não funcionará,

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

, como o awk não interpretará /search/ como uma variável, mas literalmente.

    
por branquito 21.03.2014 / 16:03

5 respostas

42

Use o operador ~ do awk e não é necessário fornecer uma regex literal no lado direito:

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

Embora isso seja mais eficiente (não é necessário ler todo o arquivo)

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

Dependendo do padrão, pode querer grep -Eq "$1"

    
por 21.03.2014 / 16:16
14
awk  -v pattern="$1" '$0 ~ pattern'

Tem um problema em que awk expande as sequências de escape ANSI C (como \n para nova linha, \f para feed de formulário, \ para barra invertida e assim por diante) em $1 . Portanto, isso se torna um problema se $1 contiver caracteres de barra invertida, o que é comum em expressões regulares. Outra abordagem que não sofre desse problema é escrevê-lo:

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

Quão ruim será depender da implementação awk .

$ 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 '.'

Todos os awk s funcionam da mesma forma para seqüências de escape válidas:

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

(o conteúdo de $a passou como está)

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

( \ foi alterado para \ e \b foi alterado para um caractere de retrocesso).

    
por 21.03.2014 / 17:16
5

Tente algo como:

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

Não, mas você pode simplesmente interpolar o padrão na string com aspas duplas que você passa para o awk:

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

Note que agora você precisa escapar do literal awk com aspas duplas, mas ainda é a maneira mais simples de realizar isso.

    
por 21.03.2014 / 16:11
-2

Você poderia usar a função eval que resolve neste exemplo a variável nets antes que o awk seja executado.

nets="searchtext"
eval "awk '/"${nets}"/'" file.txt
    
por 21.02.2017 / 12:19