Usando a variável bash com o caractere de escape no awk para extrair linhas do arquivo

5

Eu estou escrevendo um script bash (apenas aprendendo bash) para extrair algumas linhas de um arquivo baseado em dois padrões. O primeiro padrão é apenas uma sentença que termina em dois pontos. O segundo padrão é um * repetido N (neste caso 58) vezes.

Um arquivo de exemplo:

lines I don not want
lines I don not want
lines I don not want

A sentence here:
********************************************************
lines I want
lines I want
lines I want
**********************************************************

lines I don not want
lines I don not want
lines I don not want

Saída desejada:

A sentence here:
********************************************************
lines I want
lines I want
lines I want
**********************************************************

Eu posso fazer o script funcionar se eu digitar explicitamente A sentence here e \* 58 vezes dentro da chamada para o awk, mas a limpeza e a legibilidade eu prefiro fazer algo como abaixo:

pat1="A sentence here"
pat2='printf -- '\*%.s' {1..58} ; echo'
pat2=${pat2//\/\\}
awk -v pat1="${pat1}" -v pat2="${pat2}" '/{pat1}/ {p=1}; p; /{pat2}/ {p=0}' $1

Onde a primeira variável posicional é o arquivo de entrada. O código acima não retorna nada. Eu tentei inicialmente sem a substituição em pat2 , mas recebi o aviso:

awk: warning: escape sequence '\*' treated as plain '*'

Eu terei que executar este comando milhares de vezes e, idealmente, gostaria de uma solução que seja limpa e eficiente. Eu não estou amarrado a usar awk em tudo.

Editar:

Acabei de perceber que, mesmo quando digito manualmente os padrões no awk, ainda recebo a mensagem de aviso. Eu provavelmente não estou passando as variáveis para awk corretamente.

    
por dayne 20.07.2016 / 17:35

1 resposta

7

Várias opções aqui:

  • pat1, pat2 tratado como regexps:

    pat1="A sentence here"
    pat2='\*{58}'
    export pat1 pat2
    awk '$0 ~ ENVIRON["pat1"], $0 ~ ENVIRON["pat2"]'
    

    Observe que mawk e versões de gawk anteriores a 4.0.0 não suportam o operador de expressão regular estendida {} . Para versões antigas de gawk , você pode passar a variável de ambiente POSIXLY_CORRECT para reconhecê-la.

    Aqui, use a abordagem start-condition, end-condition [{action}] , mas você pode fazer o mesmo com a abordagem p flag.

  • pat1, pat2 tratado como sequências fixas:

    pat1="A sentence here"
    pat2=$(printf '*%.0s' {1..58})
    export pat1 pat2
    awk 'index($0, ENVIRON["pat1"]), index($0, ENVIRON["pat2"])'
    

    Aqui, index() procura a agulha (o conteúdo da variável) em qualquer lugar do palheiro (o registro atual (linha)), mas você também pode fazer comparação simples de linha completa:

    awk '"" $0 == ENVIRON["pat1"], "" $0 == ENVIRON["pat2"]'
    

    (o "" deve forçar uma comparação de string mesmo nos casos em que ambos $0 e ENVIRON["patx"] sejam numéricos).

Evite usar -v para passar dados que possam conter caracteres de barra invertida, pois awk faz alguma sequência de escape C ( \n , \b , \ ...) processando neles, então é necessário escapar das barras invertidas. O mesmo vale para variáveis passadas como awk '...code...' awkvar="$shellvar" . Use ENVIRON ou ARGV .

Veja esta resposta a uma questão relacionada para mais detalhes.

    
por 20.07.2016 / 18:00

Tags