teste de shell se a cadeia de várias linhas contém o padrão especificado na última linha

3

Eu quero determinar se uma string de várias linhas termina com uma linha contendo o padrão especificado.

Este código falhou, não corresponde.

s='echo hello && echo world && echo OK'
[[ "$s" =~ 'OK$' ]] && echo match
    
por gzc 17.06.2017 / 15:37

3 respostas

5

Em bash 3.2 ou superior e se a compatibilidade com 3.1 não estiver ativada (com a opção compat31 ou BASH_COMPAT=3.1 ), citando operadores de expressões regulares (não apenas com \ mas com qualquer um dos bash de operadores de cotação ( '...' , "..." , $'...' , $"..." )) remove seu significado especial.

[[ $var =~ 'OK$' ]]

corresponde apenas a strings que contêm OK$ literalmente (que $ corresponde a um literal $ )

[[ $var =~ OK$ ]]

corresponde a sequências que terminam em OK (que $ é o operador RE que corresponde ao final da sequência).

Isso também se aplica a regexps armazenados em variáveis ou o resultado de alguma substituição.

[[ $var =~ $regexp ]]   # $var matches $regexp
[[ $var =~ "$string" ]] # $var contains $string

Note que pode tornar-se complicado porque existem alguns caracteres que você precisa citar para a sintaxe do shell (como espaços em branco, < , > , & , parênteses quando não correspondidos). Por exemplo, se você deseja corresponder à .{3} <> [)}]& regexp (3 caracteres seguidos por " <> " , ) ou } e & ), é necessário algo como:

[[ $var =~ .{3}" <> "[}\)]\& ]]

Se tiver dúvidas sobre quais caracteres precisam ser citados, você sempre pode usar uma variável temporária . Isso também significa que ele tornará o código compatível com bash31 , zsh ou ksh93 :

pattern='.{3} <> [})]&'
[[ $var =~ $pattern ]] # remember *not* to quote $pattern here

Essa também é a única maneira (além de usar a opção compat31 (ou BASH_COMPAT=3.1 )) você pode fazer uso dos operadores estendidos não POSIX dos regexps do seu sistema.

Por exemplo, para que \< seja tratado como o limite de palavras que está em muitos mecanismos de expressão regular, você precisa:

pattern='\<word\>'
[[ $var =~ $pattern ]]

Fazendo:

[[ $var =~ \<word\> ]]

não funciona, pois bash trata esses \ como operadores de criação de shell e tira-os antes de passar <word> para a biblioteca regexp.

Note que é muito pior em ksh93 onde:

[[ $var =~ "x.*$" ]]
Por exemplo,

corresponderá a whatever-xa* , mas não a whatever-xfoo . A citação acima remove o significado especial para * , mas não para . nem $ .

O comportamento zsh é mais simples: as cotações não alteram o significado dos operadores regexp (como em bash31), o que contribui para um comportamento mais previsível (também pode usar regexps PCRE em vez de ERE (com set -o rematchpcre ) ).

yash não tem uma construção [[...]] , mas seu [ builtin tem um operador =~ (também em zsh ). E, claro, [ sendo um comando normal, a cotação não pode afetar a maneira como os operadores regexp são interpretados.

Observe também que, estritamente falando, o $s não contém 3 linhas, mas 2 linhas completas seguidas por uma linha não terminada. Contém hello\nworld\nOK . Na expressão regular estendida OK$ , o operador $ corresponderia apenas ao final da string .

Em uma sequência de 3 linhas completas , como hello\nworld\nOK\n (que você não seria capaz de obter com a substituição de comandos, como tiras de substituição de comandos all caracteres), o $ corresponderia após o \n , portanto, OK$ não corresponderia a ele.

Com zsh -o pcrematch , no entanto, o $ corresponde ao final da string e à nova linha no final da string, se houver uma, pois ela não passa o PCRE_DOLLAR_ENDONLY para pcre_compile . Isso pode ser visto como uma má idéia, geralmente, variáveis em shells não contêm um caractere de nova linha, e quando eles geralmente querem que eles sejam considerados dados .

    
por 17.06.2017 / 18:35
2

Pelo menos em bash , citando o RHS, força-o a ser tratado como uma comparação de string

$ s=$(printf 'hello\nworld\nOK\n')
$ echo "$s"
hello
world
OK
$ [[ "$s" =~ OK$ ]] && echo "match" || echo "no match"
match

enquanto

$ s=$(printf 'hello\nworld\nOK$\n')
$ echo "$s"
hello
world
OK$
$ [[ "$s" =~ 'OK$' ]] && echo "match" || echo "no match"
match
    
por 17.06.2017 / 15:50
0

Fato pouco conhecido: case faz isso também.

case "$(printf 'hello\nworld\nOK\n')" in
  *$'\nOK') echo "match";;
  *) echo "no match";;
esac

A string $'...' "C-style" é uma extensão Bash (que fornece um contexto onde códigos de escape de barra invertida como \n estão disponíveis em strings de shell), mas para portabiity, você pode dizer

*"
OK") echo "match";;

para obter um script de shell completamente compatível com POSIX.

Os padrões disponíveis em uma instrução case são padrões de shell glob, mas não expressões regulares adequadas.

    
por 18.06.2017 / 12:09