Estou testando isso em um sistema Debian 10 bem antigo usando
GNU Awk 4.2.1, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.1.2)
em
GNU bash, version 5.0.3(1)-release (x86_64-pc-linux-gnu)
Ambos os comandos awk
e gawk
chamam esta mesma versão do GNU Awk.
Usando essas respostas [1] [2] [3] Estou tentando escrever um script que detecta conflitos de mesclagem gettext emitidos por msgcat.
Entrada de teste de texto simples, referida abaixo como merged_file.po
:
"#-#-#-#-# de.po (Application Library) #-#-#-#-#\n"
"#-#-#-#-# de.po (Middleware Library) #-#-#-#-#\n"
"#-#-#-#-# de.po #-#-#-#-#\n"
Escolhi awk em vez de grep e sed para pular um cabeçalho usando NR > <line number>
. Como isso funciona sem problemas, omito aqui por brevidade.
Sintaxe da linha:
"#-#-#-#-#
- nome do arquivo de origem
(
Project-Id-Version)
se definido no arquivo de origem#-#-#-#-#\n"
Expressão regular construída usando RegExr e validada em todos os sabores suportados pelo regex101 : #-#-#-#-#\s+\S+\s+(?:\(([^()]+)\)\s+)?#-#-#-#-#
(Observe que isso pressupõe que o nome do arquivo não contenha espaços em branco - por enquanto, não me importo.)
O efeito pretendido é duplo:
- encontre todas as ocorrências no arquivo .po de saída para emitir uma mensagem de erro
- capture o nome da biblioteca no grupo de captura 1 para tornar a mensagem de erro mais fácil de ler (especialmente para pessoas não muito familiarizadas com gettext)
Estas são as invocações que tentei:
- A linha de base de trabalho
awk '/#-#-#-#-#\s+\S+\s+(?:\(([^()]+)\)\s+)?/ { print NR, $0 }' merged_file.po
encontra todas as ocorrências e imprime a linha inteira. awk '/#-#-#-#-#\s+\S+\s+(?:\(([^()]+)\)\s+)?#/ { print NR, $0 }' merged_file.po
descarta todas as ocorrências com Project-Id-Versionawk 'match($0, /#-#-#-#-#\s+\S+\s+(?:\(([^()]+)\)\s+)?/, library_name) { print NR, "from library \047"library_name[1]"\047" }' merged_file.po
imprime a string vazia em vez de<Project-Id-Version>
library_name[0]
contém a linha até o grupo não-capturador, então aparentementematch
não emite grupos de captura - se emitisse,library_name[0]
conteria a linha inteira.
awk '/#-#-#-#-#\s+\S+\s+(?:\(([^()]+)\)\s+)?/ { library_name = gensub(/#-#-#-#-#\s+\S+\s+(?:\(([^()]+)\)\s+)?/, "\\1", "g"); print NR, "from library \047"library_name"\047" }' merged_file.po
impressões"(<Project-Id-Version>) #-#-#-#-#\n"
em vez de<Project-Id-Version>
\\0
na verdade contém a linha inteira.\\2
contém a mesma string que\\1
though. (esperado: vazio)
O suporte a RegEx em ferramentas relacionadas, como grep ou sed, costuma ser surpreendentemente vestigial, então, em vez de apenas perguntar por que minhas invocações específicas não funcionam, prefiro perguntar de forma mais geral:
Como a correspondência de expressões regulares do GNU Awks difere da "norma"?
Respostas X/Y (definitivamente) válidas:
- Estou usando uma versão muito antiga. (Se sim: qual delas eu preciso no mínimo?)
- Sou cego e meu RegEx está quebrado. (Se sim: como?)
- A culpa é do Bash e eu preciso de algumas fugas arcanas. (Se sim: quais e por quê?)
- Baeldung está errado desta vez e, afinal, há uma solução muito mais fácil que não seja usar Awk. (Se sim: qual?)
- Para que eu não sofra o mesmo problema novamente no futuro, eu gostaria de receber este em adição a , não em vez de uma resposta à pergunta em si. Eu realmente gostaria de entender melhor o que posso esperar do Awk e o que não posso.
Parece [ 1 ][ 2 ] que qualquer expressão que comece com um parêntesis sem escape - por exemplo, um grupo de captura - e continue com um ponto de interrogação - por exemplo, um grupo não capturador - é um comportamento indefinido em Expressões Regulares Estendidas Posix e gawk em particular achou por bem... ceder.
Usando apenas grupos de captura normais, a expressão
#-#-#-#-#\s+\S+\s+(\(([^()]+)\)\s+)?#-#-#-#-#
corresponde conforme o esperado ematch($0, <expression>, library_name)
captura corretamente o grupo interno comolibrary_name[2]
.... O comportamento de
gensub
não mudou significativamente, mesmo com a expressão bem definida, mas, desde que um dos comandos funcione, acho que está "bom o suficiente".(Sugestões sobre como fazer funcionar, bem-vindo para melhorar ainda mais a resposta.)
Pediram-me para incluir a invocação e a saída resultantes, então aqui está (depois de contabilizar os caracteres de escape):
(onde
$PO
está o nome do arquivo totalmente qualificado de saída do msgcat / passado para o msgfmt)Isso gera (mantendo a convenção de nomenclatura do OP)
que parece agradável, informativo e nada ameaçador em um arquivo de log, comparado à saída de linha de base visualmente ocupada, mas nada mais útil