Comando Sed que ignoraria qualquer correspondência comentada

6

Estou tentando criar um comando sed usando regex para substituir algo em um arquivo de texto somente se ele não for comentado, mas estou tendo alguns problemas devido ao meu conhecimento quase inexistente dos comandos do sed. / p>

Encontrei soluções para pequenas partes do problema, mas algumas não estão completas o suficiente ou simplesmente não consigo colocá-las juntas. TL; versão do DR disponível no final.

Vamos primeiro determinar meu objetivo final

Gostaria de corresponder qualquer coisa (como qualquer regular regex (hehe)) em um arquivo de texto somente se NÃO for comentado. Como eu gostaria de fazer isso para vários idiomas, vamos apenas pegar os comentários comuns do C.

Portanto, neste caso, palavras ou linhas podem ser comentadas de maneiras diferentes. Temos o // para comentar somente o que vem a seguir na linha e também temos o bloco /* */ comment.

Ambiente

Atualmente estou trabalhando no Mac OSX que suporta apenas o POSIX sed, mas instalei um GNU-sed que acho melhor. (Obrigado a Homebrew . O pacote é gnu-sed e o comando é gsed .) Então, ambos estão disponíveis para mim se você preferir usar um ou outro.

Eu estou escrevendo isso assumindo que um GNU-sed é usado.

Ignorando um caso

Primeiro problema, como ignorar alguns casos. Eu achei isso facilmente em este tópico .

Agora, a parte // parece fácil para mim e eu teria que adicionar uma condição OR ( | ) para associá-la à outra condição.

Seria algo parecido com isto:

    sed -E "/\/\/.*/! s/foo/bar/" file

Então, se o arquivo de entrada for:

foo
42
test
//foo
//42
//    foo
//something foo
foo
42
something foo
  foo

A saída é:

bar
42
test
//foo
//42
//    foo
//something foo
bar
42
something bar
  bar

Então, agora, vou concentrar minha reflexão apenas no bloco /* */ comment.

Correspondendo por várias linhas

Segundo problema, como fazer a regex combinar através de várias linhas. Bem, acho que esse é o maior problema. Eu encontrei este tópico falando sobre como combinar através de apenas um novo caractere de linha. Bem, levei um momento para entender como funciona. Mas essa parte da solução me traz um novo problema e novas perguntas.

Obviamente, ele pode ignorar apenas uma nova linha ( \n ). Então agora eu quero fazer o mesmo, mas para um número desconhecido de linhas (de 0 a infinito ( * )). Aposto que tenho que passar pelas linhas, mas é aí que estou preso porque não sei nada sobre os comandos do sed e é realmente estranho para mim.

Durante minhas pesquisas, encontrei um pequeno script com o objetivo de substituir o tail comando e usa um loop (eu acho), mas não consigo entender o seu funcionamento.

Faça com que ele corresponda apenas antes da parte */

A terceira parte é garantir que o caso ignorado corresponda apenas ao final do bloco de comentários ( */ ). Portanto, no final, o caso de ignorar corresponderia apenas as coisas entre /* e */ . O comando final, então, ignoraria completamente as coisas escritas dentro de um bloco de comentários.

Eu não fiz uma pesquisa real sobre essa parte, pois não resolvi o ponto anterior e parece-me que esse problema */ depende do problema anterior /* .

Parte final: juntando tudo isso

Bem, é óbvio que eu falhei completamente nisso no momento.

TL; DR

Minha pergunta é: qual seria o comando sed para substituir qualquer coisa que desejássemos em um arquivo de texto somente se não fosse comentado?

Apêndice

Se você conhece uma maneira mais fácil de fazer isso, usando qualquer outro idioma, também é muito bem-vindo. Então, se você souber como fazer isso com awk , python ou qualquer outra coisa, sinta-se à vontade para compartilhá-lo.

    
por Vrakfall 10.04.2015 / 12:13

1 resposta

9

Você não deve acreditar neles se eles disserem que isso não pode ser feito. Você deve acreditar neles, no entanto, se eles disserem que não é fácil.

sed '\|*/|!{ s|/\*|\n&|              #if ! */ repl 1st /* w/ \n/*
     h;      s|foo|bar|g;/\n/!b      #hold; repl all foo/bar; if ! \n branch
     G;      s|\n.*\n||;:n           #Get; clear difference; :new label
     n;      \|*/|!bn;s|^|\n/*|      #new line; if ! */ branch new label
     };s|*/|\n&|g                    #repl all */ w/ \n*/
       s|foo|&\nbar|g;:r             #repl all foo w/ foo\nbar
       s|\(/\*[^\n]*\)\nbar||g;tr  #repl all /*[^\n]*\nbar w/ foo
       s|foo\n\(b\)||g             #repl all foo\nbar w/ bar
       s|^\n/.||;s|\n||g             #clear any \n inserts
'    <<\INPUT
asfoo   /* asdfooasdfoo


asdfasdfoo
asdfasdfoo
foo */foo /*foo*/ foo
/*.
foo*/
foo
hello

INPUT

OUTPUT

asbar   /* asdfooasdfoo


asdfasdfoo
asdfasdfoo
foo */bar /*foo*/ bar
/*.
foo*/
bar
hello
    
por 10.04.2015 / 13:48