Como o armazenamento da expressão regular em uma variável shell evita problemas com a citação de caracteres especiais para o shell?

3

Em Manual de bash

Storing the regular expression in a shell variable is often a useful way to avoid problems with quoting characters that are special to the shell. It is sometimes difficult to specify a regular expression literally without using quotes, or to keep track of the quoting used by regular expressions while paying attention to the shell’s quote removal. Using a shell variable to store the pattern decreases these problems. For example, the following are equivalent:

pattern=’[[:space:]]*(a)?b’
[[ $line =~ $pattern ]]

and

[[ $line =~ [[:space:]]*(a)?b ]]

If you want to match a character that’s special to the regular expression grammar, it has to be quoted to remove its special meaning. This means that in the pattern xxx.txt’, the . matches any character in the string (its usual regular expression meaning), but in the pattern "xxx.txt" it can only match a literal .. Shell programmers should take special care with backslashes, since back-slashes are used both by the shell and regular expressions to remove the special meaning from the following character. The following two sets of commands are not equivalent:

pattern=’\.’

[[ . =~ $pattern ]]
[[ . =~ \. ]]

[[ . =~ "$pattern" ]]
[[ . =~ ’\.’ ]]

The first two matches will succeed, but the second two will not, because in the second two the backslash will be part of the pattern to be matched. In the first two examples, the backslash removes the special meaning from ., so the literal . matches. If the string in the first examples were anything other than ., say a, the pattern would not match, because the quoted . in the pattern loses its special meaning of matching any single character.

Como o armazenamento da expressão regular em uma variável de shell é uma maneira útil de evitar problemas em citar caracteres que são especiais para o shell?

Os exemplos dados não parecem explicar isso. Nos exemplos fornecidos, os literais regex em um método e os valores da variável shell pattern no outro método são os mesmos.

Obrigado.

    
por Tim 27.07.2017 / 03:51

2 respostas

7

[[ ... ]] conflitos de tokenização com expressões regulares (mais sobre isso em minha resposta à sua pergunta de acompanhamento ) e \ estão sobrecarregados como uma citação de shell operador e um operador regular (com alguma interferência entre os dois no bash), e mesmo quando não há razão aparente para um choque, o comportamento pode ser surpreendente. Regras podem ser confusas.

Quem pode dizer o que isso fará sem experimentá-lo (em todas as entradas possíveis) com qualquer versão de bash ?

[[ $a = a|b ]]
[[ $a =~ a|b ]]
[[ $a =~ a&b ]]
[[ $a =~ (a|b) ]]
[[ $a =~ ([)}]*) ]]
[[ $a =~ [/\(] ]]
[[ $a =~ \s+ ]]
[[ $a =~ ( ) ]]
[[ $a =~ [ ] ]]
[[ $a =~ ([ ]) ]]

Você não pode citar os regexps, porque, se o fizer, desde o bash 3.2 e se a compatibilidade do bash 3.1 não tiver sido ativada, a citação dos regexps removerá o significado especial do operador RE. Por exemplo,

[[ $a =~ 'a|b' ]]

Corresponde se $a contiver somente a|b litteral.

Armazenar o regexp em uma variável evita todos esses problemas e também torna o código compatível com ksh93 e zsh (desde que você se limite a POSIX EREs):

regexp='a|b'
[[ $a =~ $regexp ]] # $regexp should *not* be quoted.

Não há ambigüidade na análise / tokenizing desse comando shell, e o regexp usado é aquele armazenado na variável sem qualquer transformação.

    
por 27.07.2017 / 15:43
3

A única maneira de corresponder uma string explícita é citá-la:

[[ $var =~ 'quux' ]]

Mesmo que a string contenha caracteres especiais (especiais para o shell [a] )
sem o shell expandindo ou interpretando-os [b] :

$ var='^abcd'
$ [[ $var =~ '^ab' ]] && echo yes || echo no
yes

Se precisarmos permitir (shell) caracteres especiais e permitir que o shell os interprete como uma expressão regular, eles devem ser desclassificados.

$ var='abcd'
$ [[ $var =~ ^ab ]] && echo yes || echo no
yes

Mas as strings sem aspas criam novos problemas, como os espaços:

$ var='ab cd'
$ [[ $var =~ ^ab cd ]] && echo yes || echo no
bash: syntax error in conditional expression
bash: syntax error near 'cd'

Para resolver isso, precisamos ainda citar caracteres especiais:

$ var='ab cd'
$ [[ $var =~ ^"ab cd" ]] && echo yes || echo no
yes

$ [[ $var =~ ^ab\ cd ]] && echo yes || echo no
yes

Outros exemplos:

[[ "a b"  =~  ^a\ b$ ]] && echo yes
[[ "a|b"  =~  ^a\|b$ ]] && echo yes
[[ "a&b"  =~  ^a\&b$ ]] && echo yes

Armazenar o regexp dentro de uma variável evita todos os problemas de citação.

$ regex='^a b$'
$ [[ "a b" =~ $regex ]] && echo yes
yes

[a] Lista de caracteres especiais do shell ( | & ; ( ) < > space tab newline ).

[b] Isso é verdade desde a versão bash bash-3.2-alpha (no título "3. Novos recursos no Bash") :

f. Quoting the string argument to the [[ command's =~ operator now forces string matching, as with the other pattern-matching operators.

Cópia de descrição estendida do FAQ do bash :

E14) Why does quoting the pattern argument to the regular expression matching conditional operator (=~) cause regexp matching to stop working?

In versions of bash prior to bash-3.2, the effect of quoting the regular expression argument to the [[ command's =~ operator was not specified. The practical effect was that double-quoting the pattern argument required backslashes to quote special pattern characters, which interfered with the backslash processing performed by double-quoted word expansion and was inconsistent with how the == shell pattern matching operator treated quoted characters.

In bash-3.2, the shell was changed to internally quote characters in single- and double-quoted string arguments to the =~ operator, which suppresses the special meaning of the characters special to regular expression processing (.',[', \',(', ),*', +',?', {',|', ^', and$') and forces them to be matched literally. This is consistent with how the '==' pattern matching operator treats quoted portions of its pattern argument.

Since the treatment of quoted string arguments was changed, several issues have arisen, chief among them the problem of white space in pattern arguments and the differing treatment of quoted strings between bash-3.1 and bash-3.2. Both problems may be solved by using a shell variable to hold the pattern. Since word splitting is not performed when expanding shell variables in all operands of the [[ command, this allows users to quote patterns as they wish when assigning the variable, then expand the values to a single string that may contain whitespace. The first problem may be solved by using backslashes or any other quoting mechanism to escape the white space in the patterns.

Perguntas relacionadas:

Usando uma variável em um regex

    
por 02.06.2018 / 14:30