AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 787780
Accepted
Ed Morton
Ed Morton
Asked: 2024-12-07 21:13:12 +0800 CST2024-12-07 21:13:12 +0800 CST 2024-12-07 21:13:12 +0800 CST

Por que as sequências de escape ASCII para ' são tratadas de forma diferente em grep/sed/awk?

  • 772

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:

  1. Mais importante: por que eles são diferentes?
  2. 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 -Pe sem expandir a sequência de escape antes que o grep a veja usando construções de shell como grep $'\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.html
  • sed: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/sed.html
  • grep: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/grep.html

Da especificação regexp vemos que nem xnem 0sã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 0ou xpara BREs ou EREs, então minha conclusão é que nem \x27nem \047sã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 \xnão é, então o comportamento do awk \xnã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 \0or \xe remete às definições de regexp POSIX e, portanto, \0or \xnã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, \xnã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 \x47o 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 \o047e \x27sã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.

awk
  • 1 1 respostas
  • 423 Views

1 respostas

  • Voted
  1. Best Answer
    Stéphane Chazelas
    2024-12-07T23:30:32+08:002024-12-07T23:30:32+08:00

    \1, \2, \3, \4... são usados ​​para referências anteriores em expressões regulares básicas (BRE do final dos anos 60, como encontrado em ed, grep, sed...).

    Expressões regulares estendidas introduzidas egrepno final dos anos 70 com um novo algoritmo de expressão regular não tinham (e não poderiam ter com esse algoritmo) suporte de referência anterior.

    awkcontemporâneo a egrep, com uma linguagem moldada a partir daquela Cusada em EREs desde o início, tinha literais de string semelhantes a C, dentro dos quais você podia ter \47escapes octais (como em C), e não havia nada que impedisse que esses escapes também fossem adicionados nos /ERE/literais de expressão regular, já que EREs não podiam ter referências posteriores.

    Fora de awk, nem POSIX BRE nem ERE suportam esses escapes. Only \né especificado para sed(como era historicamente suportado pelo original sed).

    \47como uma sequência de escape para o byte 0x27 definitivamente não pode ser adicionada a BREs, pois entraria em conflito com referências anteriores. Como muitas implementações ERE adicionaram suporte para referências anteriores desde o final dos anos 70, adicioná-lo ao ERE também não é mais uma opção. É uma chatice que a maioria awknão suporta referências anteriores e, naqueles que suportam, como o busybox, você tem que fazer awk '$0 ~ "^(.*)\\1$"'for o equivalente a grep -x '\(.*\)\1'(not awk '/^(.*)\1$/'as that \1is ^Ainstead and awk '/^(.*)\\1$/'is para corresponder a algo que termina em \1).

    Observe que a sintaxe em todas as ferramentas, exceto echopara aquelas sequências octais (inicialmente possivelmente de C), é \seguida por 1 a 3 números octais, não há necessidade de um 0 inicial 0e você não pode ter um 0 inicial para números de bytes acima de 63 (077). ( \0377em qualquer coisa, exceto echoé \037( ^_) seguido por 7), então enquanto \047não entra em conflito com referências anteriores, pois \0não é uma referência posterior válida (pelo menos no POSIX BRE, há alguns onde \0significa a correspondência completa), \377entraria.

    O \xHHé do C89 (ANSI C), também encontrado em perl desde perl 4 (em seus literais de string e literais regexp lá). Ele não tem aquele problema de conflito com referências posteriores, mas não é suportado por todos os mecanismos regexp ainda³. Em C, \xpode ser seguido por qualquer número de dígitos hexadecimais, já que chars em ANSI C não precisa ser de 8 bits. Em perl, apenas até 2 dígitos são aceitos, embora \x{HH}possa ser estendido para \x{20AC}¹ quando no modo Unicode também é suportado. Em outros lugares, quantos dígitos hexadecimais podem ser consumidos depois \xvaria com o aplicativo. Por exemplo, em ksh93, $'\x20AC'não é byte 0x20(espaço em ASCII) seguido por AC, mas o caractere U+20AC ( €), codificado em UTF-8².

    Os operadores de citação de shell $'\47'e $'\x27'do ksh93 agora estão no POSIXsh desde a edição de 2024 do padrão ( shainda não em todas as implementações; em particular, não dashcomo encontrados na maioria dos sistemas baseados em Debian).

    em qualquer caso, como o do awk "\47"(ou $'\x27'4 ), que se expande para o byte 0x27, portanto, apenas em 'sistemas que usam ASCII como conjunto de caracteres base, então é esse que eu evito usar, pois não vejo sentido em introduzir uma dependência em uma codificação de caracteres específica.

    O $'\u0027'(or $'\u27'or $'\U00000027') do zsh que se expande para o 'caractere quase chegou ao POSIX 2024 shtambém, mas como houve algumas divergências e diferenças entre os shells quanto ao conjunto de caracteres para o qual eles deveriam se expandir (UTF-8 incondicionalmente à la ksh93, o charmap do local no momento em que o código é lido à la bash, o charmap do local no momento em que ele é executado à la zsh) e o que fazer quando o charmap não tem o caractere correspondente, sua inclusão foi adiada para a próxima versão principal.

    Em qualquer caso 'específico, não há necessidade de especificar o código do ponto, pois ele \'pode ser usado $'...'para representar uma aspa simples.

    Com esses \OOOe \xHHagora adicionados ao sh, há ainda menos motivos para que cada ferramenta individual adicione suporte a eles.

    Minha maneira preferida de incorporar 'dentro de um argumento de código entre aspas simples sed, awk, perl, shou qualquer argumento entre aspas simples para qualquer comando, para esse caso, em shells do tipo POSIX, é inseri-lo \'fora das aspas simples:

    sh -c 'echo "It'\''s not pretty, but it works"'
    awk 'BEGIN{print "It'\''s not pretty, but it works"}'
    

    Com awk, você pode fazer:

    awk -v q="'" 'BEGIN{print "It" q "s not really prettier"}'
    

    Em rcouzsh -o rcquotes

    awk 'BEGIN{print "It''s slightly prettier"}'
    

    Em fish, você pode fazer:

    awk 'BEGIN{print "It\'s slightly prettier"}'
    

    Mas isso significa que '...'não há citações totalmente fortes lá, então, como no caso do ksh93 $'...', você tem que ficar atento ao \interior, o que é um problema.

    Outra opção é fazer:

    awk_code=$(cat << 'EOF'
    BEGIN {print "It's much more legible albeit a bit longer"}
    EOF
    )
    awk "$awk_code"
    

    ¹ \x{7fffffffffffffff}Indo muito além do intervalo Unicode, estendendo o algoritmo de codificação UTF-8.

    ² enquanto $'\xe9'expande para o byte 0xe9, não para a codificação UTF-8 de U+00E9 ( é). Isso corresponde ao comportamento do perl neste modo não-Unicode padrão, onde a codificação padrão é iso8859-1 (também conhecido como latin1, byte único), onde caracteres acima de 255 não existem, então o perl muda para UTF-8 para eles (e emite um aviso), mas no perl, quando no modo Unicode, "\xe9"expande para a codificação UTF-8 de U+00E9, enquanto no ksh93, $'\xe9'é o byte 0xe9, independentemente de estar em uma localidade UTF-8 ou não. Para gerar caracteres codificados em UTF-8 com base em seu ponto de código Unicode, é melhor usar o mais novo (do zsh) \uXXXX(ou \UXXXXXXXXXpara aqueles pontos de código acima de 0xffff)

    ³ No caso do FreeBSD sedque você mencionou, isso não é feito pelo mecanismo de expressões regulares; esses \xHHs são expandidos (em vários lugares, não apenas em expressões regulares) antes de chamar os mecanismos de expressões regulares (semelhante ao que acontece se você fizer isso subject ~ "\47"no awk), e somente desde o FreeBSD 13.0.

    4 embora tenha cuidado para garantir que o que se segue não seja mais dígitos hexadecimais, pois o comportamento do ksh93 $'\x20ac'se expande para, €como mencionado acima, é permitido, embora não exigido pelo POSIX, então se você quiser o byte 0x27 seguido por AC, você precisa $'\x27'$'AC', por exemplo (ou usar a forma octal que não tem o problema; o ksh93 suporta, $'\x{27}AC'mas essa não foi especificada pelo POSIX e, como mencionado, raramente é encontrada.

    • 9

relate perguntas

  • remova o número de linhas duplicadas com base na correspondência antes da primeira vírgula

  • anexar linhas após outros arquivos linha por linha

  • Como remover uma única linha entre duas linhas

  • Reorganize as letras e compare duas palavras

  • Embaralhamento de arquivo de várias linhas

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve