Comente todas as linhas da última linha comentada para alinhar com '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 sedoneliner para fazer isso? Ficarei feliz com qualquer ferramenta, não apenas sed, realmente.

Atualmente, estou obtendo o número da última linha comentada da seguinte forma:

$ 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 alguns bcem uma linha única feia. Certamente deve haver uma maneira mais limpa?

Respostas:


5

E se

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

7

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

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

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

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

ou seja, faça a correspondência ascendente do padrão final ao primeiro comentário - obviamente isso não é tão conveniente se você quiser uma solução no local.


4

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

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

Isso processa apenas as linhas no 1,/PATTERN/intervalo. É um e xalterações manter espaço w. padrão de espaço toda vez que uma linha é comentada (para que nunca haja mais de uma linha comentada no buffer de espera) e anexa todas as linhas que não são comentadas ao Hespaço antigo (quando na 1ª linha 1de , respectivamente, 1htambém são necessárias para remover a inicial linha vazia no buffer de espera).
Quando atinge a linha correspondente a PATTERN, também o anexa ao Hbuffer antigo, e xaltera os buffers e substitui cada \ncaractere de linha ew no espaço do padrão por um \newline e a #(ou seja, todas as linhas no espaço do padrão começam agora com #, incluindo a primeira linha como a primeira linha no espaço de espera é sempre uma linha comentada).
Com uma amostra infile:

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

corrida:

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

portanto, está comentando apenas linhas de (e excluindo) #charlieaté (e incluindo) dotane deixando as outras linhas intocadas.
Claro, isso pressupõe que sempre haja pelo menos uma linha comentada antes da correspondência da linha PATTERN. Se não for esse o caso, você poderá adicionar uma verificação adicional antes da substituição:/^#/{s/\n/&#/g}


Obrigado, terei muito o que aprender com esta resposta!
dotancohen

Espere, eu devo ter estragado tudo. Não se trata da última série de linhas comentadas? Não, entendi, é. A última série + dotan. Bastante inteligente.
18715

1
Você sempre encontra as melhores perguntas. Dotan maldito me jogou por um tempo - talvez ainda o faça, ainda não testei. obrigado, don.
mikeserv

2

Aqui está outro sed:

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

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

insira a descrição da imagem aqui

Desculpe, não sei por que fiz isso. Mas isso veio à mente.

De qualquer forma, sedespalha seus buffers entre cada última linha comentada de qualquer série, nunca retendo um único mais em seu buffer do que o necessário para rastrear com precisão a última ocorrência comentada e, a qualquer momento, encontra a última linha ao fazê-lo. A ginstrução final de execução tglobal e a ramificação são o buffer inteiro a ser impresso, caso contrário, ele criará Ptodas as linhas que liberar do buffer assim que o fizer.

Acho que foi isso que trouxe os acordeões à mente ...

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.*\)*/\1#\2/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

Há apenas uma diferença entre este comando e o acima e esse é o lcomando ook no topo. Quando nós look em sed's espaço de padrões como ele funciona, podemos ter uma melhor idéia do que se passa nos bastidores e uma melhor compreensão de como direcionar seus esforços.

Nesse caso, podemos observar a sedentrada da pilha até encontrar uma segunda ocorrência de \n#.*\ndotanentrada, e quando ela começa a imprimir a linha anterior por vez. É meio legal. Eu aprendi muito trabalhando nisso.


Muito bom, obrigado! O parágrafo final com explicações é fantástico, também passarei algum tempo aprendendo com este post. Nice stack!
13135 dotancohen

1
@ Dotancohen - esta foi uma pergunta muito boa. Dê uma olhada na edição para ver a pilha .
mikeserv

2
Percebo no histórico de edição a entrada Handle many dotans. Tenho certeza de que este é o pior pesadelo da minha esposa.
13135 dotarkhen

1
@dotancohen - sim, esta foi uma pergunta difícil. Coisas como #\ndotan\ndotané difícil para essas coisas. Eu falo sério quando digo que é uma boa pergunta. Eu acho que eu consegui quase perfeito, mas um problema que poderia correr em é se seus blocos de comentário estão separados por 1000 linhas - que irá retardá-lo. Você pode colocar algo como s/\n/&/150;tantes da primeira /\n#coisa a quebrar o buffer se ele se estender por 150 linhas, por exemplo. E de qualquer maneira, talvez seja exatamente o que ela estava esperando o tempo todo !
18715
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.