t descascando comentários em linha

1

Eu tenho um script simples para remover comentários de um arquivo js:

#!/bin/bash
sed -E '/^[[:blank:]]*(\/\/|#)/d;s/#.*//' $1 >> stripped.js

isso funciona quase perfeito, exceto por comentários que ocorrem em linha, como

// file-to-be-stripped.js
...
...
const someVar = 'var' // this comment won't be stripped
// this comment will be stripped

O que estou perdendo para despir comentários in-line?

ATUALIZAÇÃO:

O que é realmente estranho é que eu atirei o seu exemplo com um shell bash online e ele funciona perfeitamente! No entanto, quando eu executo o mesmo código localmente, ele não tira os inline !? Alguma idéia de por que / como isso poderia ser? Eu obviamente estou sentindo falta de algo ... muito estranho. Aqui está o meu código atualizado usando a resposta dada pelo @AFSHIN.

Meu script: stripper.sh

#!/bin/bash
sed -E -e 's:(\s+(//|#)|^\s*(//|#)).*$::; /^$/d' $1 > "stripped.${1}"

Meu arquivo de teste: test.js

// testies one
const testies = 'two'
console.log(testies) // three
// testies FOUR!?
console.log('Mmmmm toast') // I won't be stripped of my rights!

Então eu executo: ./stripper.sh test.js e a saída é:

const testies = 'two'
console.log(testies) // three
console.log('Mmmmm toast') // I won't be stripped of my rights!

Alguma idéia de por que executar exatamente o mesmo código localmente apenas comentários completos de linha do sed, mas ainda executá-lo com o intérprete on-line ( infelizmente eu não posso compartilhar o link exato para o meu shell porque é um link bit.ly e aparentemente é um "não não" aqui.) funciona como esperado?

    
por archae0pteryx 04.08.2017 / 17:35

4 respostas

4

POSIXly, você faria:

sed '
  s|[[:blank:]]*//.*||; # remove //comments
  s|[[:blank:]]*#.*||; # remove #comments
  t prune
  b
  :prune
  /./!d; # remove empty lines, but only those that
         # become empty as a result of comment stripping'

Com o GNU sed , podemos reduzir para:

sed -E 's@[[:blank:]]*(//|#).*@@;T;/./!d'

Note que seria feliz remover #things e //things que não são comentários como em:

const url = 'http://stackexchange.com';
x = "foo#bar";

Para ignorar as citações internas # , // , você poderia fazer:

perl -ne 'if (/./) {
   s{\s*(?://|#).*|("(?:\.|[^"])*"|'"'(?:\\.|[^'])*'"'|.)}{$1}g;
   print if /./} else {print}'

Em uma entrada como:

#blah
// testies one
const testies = 'two';
console.log(testies) // three

const url = 'http://stackexchange.com';
x = "not#a comment";
y = "foo\"bar" # comment
y = 'foo\'bar' # it's a comment

Dá:

const testies = 'two';
console.log(testies)

const url = 'http://stackexchange.com';
x = "not#a comment";
y = "foo\"bar"
y = 'foo\'bar'

(talvez seja necessário adaptar-se ao idioma real desses arquivos (não estou ciente de que o JavaScript suporta # como um comentário, exceto talvez para uma primeira linha começando com #! em node.js)).

    
por 04.08.2017 / 22:38
2
sed -e '/^\/\//d' -e 's@\(.*\)[[:blank:]]\{1,\}//.*@@' your_file

Esse comando sed exclui as linhas que começam com um comentário e, para comentários embutidos, remove tudo, desde o espaço em branco que separa o código do comentário até o final da linha. É POSIX (sem extensões GNU usadas) e, de acordo com o exemplo original do OP e para facilitar a leitura, esta versão suporta apenas // comentários (mais sobre isso abaixo).

Detalhes

Essa chamada sed inclui dois comandos sed: um "excluir no padrão" e uma substituição.

O primeiro é /^\/\//d . O padrão ^\/\/ corresponde a linhas que começam com duas barras (por exemplo, "// foo bar"). Essas linhas são excluídas e a próxima linha é inserida imediatamente (ou seja, a substituição é ignorada).

O padrão na substituição é \(.*\)[[:blank:]]\{1,\}//.* . Nota: Estou usando @ como um delimitador para evitar que algum caractere escape que um delimitador / exigiria.

  • \( .. \) - qualquer coisa correspondida está disponível como referência de volta
  • .* - corresponde a 0 ou mais caracteres (nada além de nova linha); na seção de substituição, podemos nos referir ao que quer que seja correspondido aqui por causa dos \( e \) .
  • [[:blank:]] - um caractere de espaço em branco
  • \{1,\} - corresponde a uma ou mais das coisas que o precedem ( [[:blank:]] neste caso)
  • // - combina duas barras (ou seja, o começo de um comentário)
  • .* - o mesmo que acima, exceto não disponível como referência anterior

A parte de substituição é apenas , que diz para substituir o que quer que tenhamos correspondido com a primeira referência anterior, ou seja, a .* que precedeu [[:blank:]] .

Por isso, funciona exatamente como descrevi: para comentários incorporados, remova tudo, desde o espaço em branco que separa o código do comentário até o final da linha.

'#' Comentários

Com o GNU sed, adicionar manipulação de # comentários é apenas uma questão de substituir // pela alternância (#|//) (ou se precisarmos de escapar \(#\|\/\/\) ). Para fazê-lo, o modo POSIX, no entanto, é muito mais detalhado porque a alternância não é suportada. Você poderia obviamente forçá-lo repetindo os comandos sed existentes com versões para # . Melhor ainda, já existe uma resposta que mostra uma maneira mais clara de fazê-lo. De qualquer forma, não vou repetir uma solução aqui.

EDITAR:

Tendo revisitado isso depois de muito tempo eu percebo que a substituição é mais complicada do que precisa ser e, como apontado nos comentários, não pega certos casos de canto além (por exemplo, "algo // foo // bar" ..only "// bar" é removido).

Eu acredito que isso é tudo que precisamos ...

sed -e '/^\/\//d' -e 's@[[:blank:]]\{1,\}//.*@@' your_file

Ou seja, a parte de substituição diz "no primeiro espaço em branco que encontramos, removê-lo e tudo o que segue, deixando apenas qualquer texto anterior".

    
por 04.08.2017 / 18:19
0

Usando sed para preservar os links e os dois tipos de comentários // e # :

sed -E -e 's:(\s\{1,\}(//|#)|^\s*(//|#)).*$::; /^$/d' infile.txt

Ou se os comentários forem apenas com // , então:

sed -E -e 's:(\s\{1,\}//||^\s*//).*$::; /^$/d' infile.txt

E se não houver um link ou você não se importar com os links, ele será ainda mais curto:

sed -E -e 's:\s*//.*$::; /^$/d' infile.txt
  • A parte s:(\s\{1,\}(//|#)|^\s*(//|#)).*$:: está sendo substituída pela string vazia se abaixo estiver relacionada à vista.
    1. \s\{1,\}(//|#) : quaisquer linhas seguidas por // ou ( | ) # com um ou mais espaços em branco antes, isso preservará os links como https://domain (obrigado @Phillippos apontou isso)
    2. ou |^\s*(//|#) : todas as linhas começam com espaços em branco seguidos por // ou #
    3. .*$ corresponde ao item 1 ou item2 acima e depois disso até o final da linha .*$
    4. /^$/d : remove linhas vazias. Se você quiser remover linhas que incluam espaços em branco, substitua-as por /^\s*$/d .
por 04.08.2017 / 18:19
0

Usando GNU sed , podemos criar um código de mini-analisador para filtrar C++ style comments, // e sh style comments # também.

Para tornar a construção modular e escalonável, utilizamos regexes enlatadas definidas em variáveis de shell e adequadamente citadas.

O código sed , vamos passar linhas vazias / em branco. Em seguida, ele procura por aspas duplas desequilibradas em linhas. Ele continua agarrando as próximas linhas até que elas se equilibrem. Isso é para propósitos de citações que se espalham por várias linhas.

O mesmo é feito para aspas simples também.

Em seguida, procuramos por linhas de continuação, identificadas por meio de uma barra invertida.

Finalmente, continuamos pulando as palavras citadas ou barewords que não são comentários.

Se, após essa transformação, ficarmos sem nada, então a excluímos, a OTW imprime prontamente a linha decommentified para stdout.

PS: Usamos uma mistura de aspas simples e duplas no sed -e ... para solucionar o erro nas linhas de comando bash , onde o caractere ! dentro das aspas duplas é insuperável, por isso o colocamos em aspas simples.

# symbol names
q=\' Q=\"
d=\$ b=\
B=$b$b

# construct regexes using symbolic names
single_quotes_open="$q[^$b$q]*($B.[^$b$q]*)*$d"
single_quoted_word="$q[^$b$q]*($B.[^$b$q]*)*$q"
double_quoted_word="$Q[^$b$Q]*($B.[^$b$Q]*)*$Q"
double_quotes_open="$Q[^$b$Q]*($B.[^$b$Q]*)*$d"
quoted_word="$double_quoted_word|$single_quoted_word"

# decomment a c++ file
sed -Ee '
   /\S/!b'"
   :a;/(^|\s)$double_quotes_open/{N;ba;}
   :b;/(^|\s)$single_quotes_open/{N;bb;}
   :c;/$B$d/{N;bc;}
   s_\s*(//|#).*|($quoted_word|.)__g
   "'/\S/!d
' c_file
    
por 06.08.2017 / 04:37