Comente todas as linhas da última linha comentada com a linha 'foo'

12

Considere um arquivo de texto users.txt :

#alice
#bob
charlie
dotan
eric

Eu preciso comentar tudo, desde (exclusivo) a última linha comentada até (inclusive) dotan . Este é o resultado:

#alice
#bob
#charlie
#dotan
eric

Existe um bom sed oneliner para fazer isso? Eu ficarei feliz com qualquer ferramenta, não apenas sed , realmente.

Atualmente, estou recebendo o número da última linha comentada assim:

$ cat -n users.txt | grep '#' | tail -n1
  2 #bob

Depois, adiciono um e comento com sed :

$ sed -i'' '3,/dotan/ s/^/#/' users.txt

Eu sei que eu poderia ser inteligente e juntar tudo isso com um pouco de bc em um one-liner feio. Certamente deve haver um caminho mais limpo?

    
por dotancohen 21.07.2014 / 14:49

4 respostas

5

Que tal

perl -pe '$n=1 if s/^dotan/#$&/; s/^[^#]/#$&/ unless $n==1;' file

ou a mesma ideia no awk:

awk '(/^dotan/){a=1; sub(/^/,"#",$1)} (a!=1 && $1!~/^#/){sub(/^/,"#",$1);}1; ' file
    
por 21.07.2014 / 15:04
7

Se as linhas comentadas existentes formarem um único bloco contíguo, você poderá compará-las a partir da primeira linha comentada, comentando apenas as linhas até e incluindo o padrão final que ainda não foram comentadas

sed '/^#/,/dotan/ s/^[^#]/#&/' file

Se os comentários existentes não forem contíguos, então, devido à natureza gananciosa da correspondência do intervalo sed, acho que você precisaria fazer algo como

tac file | sed '/dotan/,/^#/ s/^[^#]/#&/' | tac

i.e. combinar para cima do padrão final para o 'primeiro' comentário - obviamente isso não é tão conveniente se você quiser uma solução no local.

    
por 21.07.2014 / 15:24
4

Você pode manipular ambos os casos (linhas comentadas em um único bloco contíguo ou intercaladas entre linhas não comentadas) com uma única invocação sed :

sed '1,/PATTERN/{/^#/{x;1d;b};//!{H;/PATTERN/!{1h;d};//{x;s/\n/&#/g}}}' infile

Isso processa somente as linhas no intervalo 1,/PATTERN/ . Ele e x alterações retêm espaço w. espaço padrão toda vez que uma linha é comentada (portanto, nunca há mais de uma linha comentada no buffer de retenção) e anexa todas as linhas que não são comentadas no espaço H old (quando na primeira linha, 1d e% respectivamente1h também são necessários para remover a linha vazia inicial no buffer de retenção). Quando ele atinge a linha PADRÃO correspondente, ele também o anexa ao buffer H old, e x altera os buffers e, em seguida, substitui cada caractere de \n ewline no espaço de padrão por \n ewline e # (ou seja, todas as linhas no espaço padrão agora começarão com # , incluindo a primeira linha, pois a primeira linha no espaço de retenção é sempre uma linha comentada). Com uma amostra infile :

alice
#bob
bill
#charlie
ding
dong
dotan
jimmy
#garry

em execução:

sed '1,/dotan/{                   # if line is in this range    -start c1
/^#/{                             # if line is commented        -start c2
x                                 # exchage hold space w. pattern space
1d                                # if 1st line, delete pattern space
b                                 # branch to end of script
}                                 #                             -end c2
//!{                              # if line is not commented    -start c3
H                                 # append to hold space
/dotan/!{                         # if line doesn't match dotan -start c4
1h                                # if 1st line, overwrite hold space
d                                 # delete pattern space
}                                 #                             -end c4
//{                               # if line matches dotan       -start c5
x                                 # exchage hold space w. pattern space
s/\n/&#/g                         # add # after each newline character
}                                 #                             -end c5
}                                 #                             -end c3
}' infile                         #                             -end c1

saídas:

alice
#bob
bill
#charlie
#ding
#dong
#dotan
jimmy
#garry

então está comentando apenas linhas de (e excluindo) #charlie até (e incluindo) dotan e deixando as outras linhas intactas.
Claro, isso pressupõe que sempre haja pelo menos uma linha comentada antes da linha correspondente a PATTERN . Se esse não for o caso, você pode adicionar uma verificação adicional antes da substituição: /^#/{s/\n/&#/g}

    
por 13.07.2015 / 13:41
2

Aqui está outro sed :

sed  -e:n -e'/\n#.*\ndotan/!{$!{N;/^#/bn'      \
-eb  -e\} -e'/^#/s/\(\n\)\(dotan.*\)*/#/g' \
-et  -e\} -eP\;D <in >out

Isso faz o que você pede. Ele apenas funciona em uma pilha - construindo-a quando necessário e pelo tempo que for necessário entre ocorrências de linhas comentadas, e descarregando o buffer antigo em favor da nova linha comentada mais adiante na entrada quando encontrar uma. Imagem ...

Desculpe,nãoseiporquefizisso.Masveioàmente.

Dequalquerforma,sedespalhaseusbuffersentrecadaúltimalinhacomentadaemqualquersérie,nuncaretendomaisnadaemseubufferdoqueonecessáriopararastrearcomprecisãoaúltimaocorrênciacomentadaeseaqualquermomentoencontraraúltimalinhaenquantoaofazerisso,eletentaráainstruçãodeexecuçãofinalglobalearamificaçãotesttodoobufferaserimpresso,senãoPrinttodasaslinhasqueeleliberardeseubufferassimqueocorrer./p>

Euachoqueissoéoquetrouxeosacordeõesparaamente...

printf %s\n \#alice \#bob charlie dotan eric \ \#alice \#bob charlie dotan eric \ \#alice \#bob charlie dotan eric | sed -e:n -e'l;/\n#.*\ndotan/!{$!{N;/^#/bn' \ -eb -e\} -e'/^#/s/\(\n\)\(dotan.*\)*/#/g' \ -et -e\} -eP\;D
#alice
#alice\n#bob$
#alice\n#bob\ncharlie$
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob\ncharlie\ndotan\neric\n#alice$
#bob\ncharlie\ndotan\neric\n#alice\n#bob$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
#bob
charlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
charlie
dotan\neric\n#alice\n#bob\ncharlie\ndotan$
dotan
eric\n#alice\n#bob\ncharlie\ndotan$
eric
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob\ncharlie\ndotan\neric\n#alice$
#bob\ncharlie\ndotan\neric\n#alice\n#bob$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
#bob
charlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
charlie
dotan\neric\n#alice\n#bob\ncharlie\ndotan$
dotan
eric\n#alice\n#bob\ncharlie\ndotan$
eric
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob
#charlie
#dotan
eric

Existe apenas uma diferença entre este comando e o que está acima e esse é o comando l ook no topo. Quando nós l ook no espaço de padrão de sed como funciona, podemos ter uma idéia melhor do que acontece nos bastidores e uma melhor compreensão de como direcionar seus esforços.

Nesse caso, podemos assistir sed stack input até encontrar uma segunda ocorrência de \n#.*\ndotan na entrada e quando começar a imprimir a saída anterior de uma linha por vez. É legal. Eu aprendi muito trabalhando nisso.

    
por 13.07.2015 / 17:54