Usando a versão GNU de todas as 3 ferramentas (role para baixo para ver as tentativas do FreeBSD), se eu quisesse encontrar '
na entrada usando o awk com um '
script delimitado por -, poderíamos tentar corresponder usando as sequências de escape hexadecimais e octais:
$ echo "'" | awk '/\x27/'
'
$ echo "'" | awk '/\047/'
'
$ echo "'" | awk '/\o047/'
awk: cmd. line:1: warning: regexp escape sequence `\o' is not a known regexp operator
então os 2 primeiros funcionam e o 3º não, como você esperaria intuitivamente.
Agora vamos tentar o mesmo com sed (com ou sem -E
):
$ echo "'" | sed -n '/\x27/p'
'
$ echo "'" | sed -n '/\047/p'
$
$ echo "'" | sed -n '/\o047/p'
'
e grep (também com ou sem -E
):
$ echo "'" | grep '\x27'
grep: warning: stray \ before x
$ echo "'" | grep '\047'
grep: warning: stray \ before 0
$ echo "'" | grep '\o047'
grep: warning: stray \ before o
Então:
- Mais importante: por que eles são diferentes?
- Curiosidade secundária: Existe uma maneira de usar uma sequência de escape no grep para corresponder
'
sem recorrer à opção não portátil do GNU grep-P
e sem expandir a sequência de escape antes que o grep a veja usando construções de shell comogrep $'\047'
?
Vale ressaltar que octal \047
é a sequência de escape recomendada no awk (veja http://awk.freeshell.org/PrintASingleQuote ou https://web.archive.org/web/20230530010453/http://awk.freeshell.org/PrintASingleQuote se estiver inativo).
Para os propósitos desta questão, não estou interessado em alternativas que permitam literal '
ou o que qualquer outra ferramenta faz ou qualquer outra coisa, estou apenas tentando descobrir por que essas 3 ferramentas específicas de correspondência de regexp tratam sequências de escape ASCII de forma diferente umas das outras. Eu estaria, no entanto, interessado em aprender como o BSD ou outras variantes dessas 3 ferramentas se comportam, dados os mesmos scripts mostrados acima.
Informações adicionais:
FreeBSD
Este é o comportamento do FreeBSD 13.1:
% echo "'" | awk '/\x27/'
'
% echo "'" | awk '/\047/'
'
% echo "'" | sed -n '/\x27/p'
'
% echo "'" | sed -n '/\047/p'
% echo "'" | sed -n '/\o047/p'
sed: 1: "/\o047/p": RE error: trailing backslash (\)
% echo "'" | grep '\x27'
grep: trailing backslash (\)
% echo "'" | grep '\047'
%
POSIX
Veja o que os padrões POSIX para Expressões Regulares e as 3 ferramentas em questão dizem sobre isso:
- expressões regulares: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap09.html
awk
: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/awk.htmlsed
: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/sed.htmlgrep
: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/grep.html
Da especificação regexp vemos que nem x
nem 0
são "Caracteres Especiais" em um BRE ou ERE , portanto são "Caracteres Ordinários" e que
Quando não está dentro de uma expressão entre colchetes, a interpretação de um caractere comum precedido por um caractere sem escape é indefinida, exceto para:
seguido por listas de caracteres, nenhum dos quais inclui 0
ou x
para BREs ou EREs, então minha conclusão é que nem \x27
nem \047
são comportamentos definidos em uma expressão regular por POSIX.
A seção Expressões Regulares da especificação POSIX awk diz:
\ddd
Um caractere seguido pela sequência mais longa de um, dois ou três caracteres de dígito octal (01234567). Se todos os dígitos forem 0 (ou seja, representação do caractere NUL), o comportamento é indefinido. Se os dígitos produzirem um valor maior que o octal 377, o comportamento é indefinido.
então sabemos que \0
é definido para um awk POSIX, mas \x
não é, então o comportamento do awk \x
não é definido pelo POSIX para o awk e, portanto, é deixado para as várias implementações do awk.
A seção Expressões regulares da especificação POSIX sed adiciona algumas alterações ao regexp, mas não menciona \0
or \x
e remete às definições de regexp POSIX e, portanto, \0
or \x
não são definidos pelo POSIX para sed.
A seção Descrição da especificação grep do POSIX se refere inteiramente às definições de expressões regulares do POSIX e, portanto \0
, \x
não são definidas pelo POSIX para grep.
Então, aparentemente, o significado de \xdd
é deixado para os implementadores de ferramentas para grep, sed e awk, enquanto o significado de \0dd
é definido para awk, mas deixado para os implementadores de grep e sed.
Manuais GNU
A seção Sequências de escape do manual do GNU awk diz:
\nnn
O valor octal nnn, onde nnn representa de 1 a 3 dígitos entre '0' e '7'. Por exemplo, o código para o caractere ASCII ESC (escape) é '\033'.
\xhh…
O valor hexadecimal hh, onde hh representa uma sequência de dígitos hexadecimais ('0'–'9', e 'A'–'F' ou 'a'–'f'). São permitidos no máximo dois dígitos após '\x'...
então é aí que \x47
o GNU awk é definido.
A seção Sequências de Escape do manual do GNU sed diz:
\oxxx
Produz ou corresponde a um caractere cujo valor ASCII octal é xxx.
\xxx
Produz ou corresponde a um caractere cujo valor hexadecimal ASCII é xx.
então é onde \o047
e \x27
são definidos para o GNU sed.
O manual do GNU grep não contém nenhuma referência que eu tenha encontrado sobre sequências de escape hexadecimais ou octais, o que explica as mensagens de aviso que vemos quando tentamos usá-las e provavelmente significa que elas simplesmente não são suportadas no GNU grep.