Comente todas las líneas desde la última línea comentada hasta la línea con 'foo'

12

Considere un archivo de texto users.txt:

#alice
#bob
charlie
dotan
eric

Necesito comentar todo, desde (exclusivo) la última línea comentada hasta (incluido) dotan. Este es el resultado:

#alice
#bob
#charlie
#dotan
eric

¿Hay una buena línea sedpara hacer esto? Seré feliz con cualquier herramienta, no solo sed, de verdad.

Actualmente estoy obteniendo el número de línea de la última línea comentada así:

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

Luego agrego uno y comento con sed:

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

Sé que podría ser inteligente y poner todo esto junto con algunos bcen una sola frase fea. Seguramente debe haber una forma más limpia?

dotancohen
fuente

Respuestas:

5

Qué tal si

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

o, la misma idea en awk:

awk '(/^dotan/){a=1; sub(/^/,"#",$1)} (a!=1 && $1!~/^#/){sub(/^/,"#",$1);}1; ' file
terdon
fuente
7

Si las líneas comentadas existentes forman un solo bloque contiguo, entonces podría coincidir desde la primera línea comentada, comentando solo aquellas líneas hasta e incluyendo su patrón final que aún no están comentados

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

Si los comentarios existentes no son contiguos, entonces, debido a la naturaleza codiciosa de la coincidencia de rango sed, creo que debería hacer algo como

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

es decir, hacer coincidir hacia arriba desde el patrón final hasta el "primer" comentario; obviamente, eso no es tan conveniente si quieres una solución in situ.

conductor de acero
fuente
4

Puede manejar ambos casos (líneas comentadas en un solo bloque contiguo o intercaladas entre líneas no comentadas) con una sola sedinvocación:

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

Esto procesa solo las líneas en el 1,/PATTERN/rango. Se ae xcambios conservan el espacio w. espacio de patrón cada vez que se comenta una línea (por lo que nunca hay más de una línea comentada en el búfer de retención) y agrega cada línea que no está comentada al Hespacio anterior (cuando está en la primera línea, 1dy respectivamente 1h, también son necesarias para eliminar la inicial línea vacía en el búfer de retención).
Cuando alcanza el PATTERN que coincide con la línea, también lo agrega al Hbúfer anterior, e xcambia los búferes y luego reemplaza cada \ncarácter de línea ewline en el espacio del patrón con una \newline y a #(es decir, todas las líneas en el espacio del patrón ahora comenzarán con #, incluir la primera línea como la primera línea en el espacio de espera siempre es una línea comentada).
Con una muestra infile:

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

corriendo:

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

salidas:

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

así que solo comenta líneas desde (y excluye) #charliehasta (e incluye) dotany deja las otras líneas intactas.
Claro, esto supone que siempre hay al menos una línea comentada antes de la coincidencia de línea PATTERN. Si ese no es el caso, puede agregar un cheque adicional antes del reemplazo:/^#/{s/\n/&#/g}

don_crissti
fuente
¡Gracias, tendré mucho que aprender de esta respuesta!
dotancohen
Espera, debo haber jodido. ¿No se trata de la última serie de líneas comentadas? No, lo entiendo, lo es. La última serie + dotan. Bastante inteligente.
mikeserv
1
Siempre encuentras las mejores preguntas. Maldito dotan me hizo tirar por un tiempo, tal vez todavía lo hace, aún no lo he probado. gracias don
mikeserv
2

Aquí hay otro sed:

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

Eso hace lo que pides. Simplemente funciona en una pila, construyéndolo cuando sea necesario y durante el tiempo que sea necesario entre las ocurrencias de las líneas comentadas, y volcando el búfer viejo a favor de la nueva línea comentada más adelante en la entrada cuando encuentra una. Imagen...

ingrese la descripción de la imagen aquí

Lo siento, no sé por qué hice eso. Pero me vino a la mente.

De todos modos, sedextiende sus búferes entre cada última línea comentada en cualquier serie, sin retener nunca más en su búfer de lo necesario para rastrear con precisión la última ocurrencia comentada, y si en algún momento encuentra la última línea mientras lo hace, intentará la gdeclaración final de ejecución lobal y la ramificación testán imprimiendo todo el búfer, de lo contrario borrará Ptodas las líneas que libera de su búfer tan pronto como lo haga.

Supongo que esto es lo que me hizo pensar en los acordeones ...

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

Solo hay una diferencia entre este comando y el anterior y ese es el lcomando ook en la parte superior. Cuando lOOK en sed's espacio de patrones, ya que funciona podemos tener una mejor idea de lo que sucede detrás de las escenas y una mejor comprensión de cómo dirigir sus esfuerzos.

En este caso, podemos observar la sedentrada de la pila hasta que haya encontrado una segunda aparición de \n#.*\ndotanentrada, y eso cuando comience a imprimir la línea anterior de una en una. Es un poco genial. Aprendí mucho trabajando en esto.

mikeserv
fuente
¡Muy bonito, gracias! El párrafo final con explicaciones es excelente, también pasaré bastante tiempo aprendiendo de esta publicación. Buena pila!
dotancohen
1
@dotancohen: esta fue una muy buena pregunta. Echa un vistazo a la edición para ver la pila .
mikeserv
2
Noto en la historia de edición la entrada Handle many dotans. Estoy seguro de que esta es la peor pesadilla de mi esposa.
dotancohen
1
@dotancohen: sí, fue difícil. Cosas como #\ndotan\ndotanes difícil para estas cosas. Lo digo en serio cuando digo esto, una buena pregunta. Yo creo que lo tengo casi perfecto, pero un problema que podría ejecutar en es si sus bloques de comentarios están separados por 1000 líneas - que va a reducir la velocidad. Puede pegar algo como s/\n/&/150;tantes de lo primero /\n#para romper el búfer si abarca 150 líneas, por ejemplo. Y de todos modos, ¡tal vez es justo lo que ha estado esperando todo el tiempo !
mikeserv