Considere a sequência hello(world)
.
Eu queria usar uma awk
invocação para extrair arquivos world
.
Esta foi minha primeira tentativa e a saída foi uma string vazia, sem erros.
echo 'hello(world)' | awk -F'(|)' '{print $2}'
Minha segunda tentativa foi usar classes de caracteres, que produziram o comportamento esperado:
echo 'hello(world)' | awk -F'[()]' '{print $2}'
No entanto, dado que a documentação do awk afirma que o separador de campos pode ser uma expressão regular, eu esperava que a primeira tentativa funcionasse.
Aqui está minha versão awk:
$ awk --version
GNU Awk 5.0.1, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.2.0)
Copyright (C) 1989, 1991-2019 Free Software Foundation.
Por que a expressão (|)
não alcança o resultado desejado?
O regexp
(|)
significa o mesmo que|
, the(
e)
apenas coloque-o em um grupo desnecessariamente. Os metachares de agrupamento(
e)
regexp são úteis para escrever regexps, como(a|b)c
dizer, quec
podem ser precedidos pora
oub
, ou para casos em que você precisa de grupos de captura para referência posterior posteriormente, mas não aqui.Se você não deseja
(
ser)
tratado como metachars regexp, então você precisa escapar da abertura(
, e apenas da abertura(
, porque o fechamento)
é apenas um metachar regexp se seguir uma abertura sem escape(
, assim como]
é apenas um metachar regexp se for segue uma abertura sem escape[
. Se você optar por escapar também do)
comportamento]
tecnicamente indefinido por POSIX, assim como escapar de qualquer outro caractere literal, embora eu nunca tenha encontrado pessoalmente uma variante awk que não o tratasse apenas como um caractere literal.Assim como quando você escreve um regexp dinâmico , quando você escreve uma string Field Separator, ela passa por 2 fases de análise, primeiro quando a string é convertida em um regexp/separador de campo onde você a define, e novamente quando é usada como tal durante execução do código. Dado que, se você quiser
\
escapar dos metachares em um FS, você precisa escapá-los duas vezes, ou seja\\
, . Alternativamente, em vez de colocar 2 barras invertidas antes do metachar, você poderia colocá-lo dentro de uma expressão de colchetes[...]
e isso também o tornaria literal. A IMO faz o último torna seu código mais claro do que duplicar escapes e[)]
não tem o pequeno problema que\\)
é um comportamento tecnicamente indefinido se você decidir "escapar" de ambos os caracteres.Então, para fazer
(
e)
literal você pode escrever qualquer um destes em qualquer awk:awk -F'\\(|)' '{print $2}'
awk -F'[(]|)' '{print $2}'
awk -F'[()]' '{print $2}'
com minha preferência FWIW sendo o número 3, mesmo que
)
seja literal dentro ou fora da expressão entre colchetes, pois é mais conciso que as 2 primeiras alternativas.Depois de brincar com a apresentação
\
de vários personagens, finalmente percebi que o problema era com o personagem()
e não com o|
personagem. Além disso, era()
necessário escapar duas vezes.A seguinte expressão alcança o resultado desejado:
Curiosamente, omitir o escape no parêntese de fechamento também parece funcionar corretamente:
Dos operadores Regexp em
awk
levando isso em consideração, você pode obter o efeito desejado escrevendo