Estaba mirando esta pregunta y luego me preguntaba cómo podría implementar mi respuesta, que usa
sed
puramente
POSIX
ex
.
El truco es que, si sed
bien puedo comparar el espacio de espera con el espacio del patrón para ver si son exactamente equivalentes (con
G;/^\(.*\)\n\1$/{do something}
), no conozco ninguna forma de hacer tal prueba ex
.
Sé que en Vim podría Y
marcar la primera línea y luego escribir
:2,$g/<C-r>0/d
para hacer casi lo que estoy especificando, pero si la primera línea contiene algo que no sea un texto alfanumérico muy directo, esto se convierte realmente en un cambio, ya que la línea se está volcando como una
expresión regular. , no solo una cadena para comparar. (¡Y si la primera línea contiene una barra diagonal, el resto de la línea se interpretará como un comando!)
Entonces, si deseo eliminar todas las líneas myfile
que son idénticas a la primera línea, pero no eliminar la primera línea, ¿cómo podría hacerlo usando ex
? Para el caso, ¿cómo podría hacerlo usando vi
?
¿Hay alguna forma POSIX de eliminar una línea si coincide exactamente con otra línea?
Quizás algo como esta sintaxis imaginaria:
:2,$g/**lines equal to "0**/d
fuente
:execute '2,$g/\V' . escape(getline(1), '\') . '/d'
sed
como un filtro desde adentroex
y ejecutar toda mised
respuesta en todo el búfer ... lo que funcionaría, por supuesto (y en realidad es portátil a diferenciased -i
).<C-r>0
muy bueno. No estoy seguro de que pueda hacerlo mejor solo con los comandos Ex porque debe proteger los caracteres especiales. Sin la restricción compatible con POSIX, creo que usaría el interruptor muy nomagic\V
y luego protegería la barra invertida (porque mantiene su significado especial incluso con\V
) con laescape()
función cuyo segundo argumento es una cadena que contiene todos los caracteres que desea escapar / proteger .:execute '2,$g/\V' . escape(getline(1), '\/') . '/d'
O podría usar otro carácter para el delimitador de patrón como un punto y coma. En este caso, no necesitaría proteger una barra diagonal en el patrón. Daría algo como::execute '2,$g;\V' . escape(getline(1), '\') . ';d'
sed
también muy bueno. Con Vim, a menudo delega ciertas tareas especiales a otros programas, ysed
probablemente sea un buen ejemplo de eso. Por cierto, no tiene que ejecutarsed
todo su búfer. Si desea ejecutarlo solo en una parte del búfer, puede asignar un rango. Por ejemplo, si desea filtrar sólo las líneas entre 50 y 100, podría escribir::50,100!<your sed command>
.Respuestas:
Empuje
En Vim puedes combinar cualquier personaje, incluida la nueva línea con
\_.
. Puede usar esto para construir un patrón que coincida con una línea completa, cualquier cantidad de cosas y luego esa misma línea:Ahora desea eliminar todas las líneas de un archivo que coincidan con la primera, sin incluir la primera. La sustitución para eliminar la última línea que coincide con la primera es:
Puede usar
:global
para asegurarse de que la sustitución se repita suficientes veces para eliminar todas las líneas:POSIX ex
@saginaw muestra una forma más ordenada de hacer esto en Vim en un comentario a su pregunta, pero podemos adaptar la técnica anterior para POSIX ex.
Para hacer esto de una manera compatible con POSIX, debe rechazar la coincidencia de varias líneas, pero aún puede hacer uso de referencias posteriores. Esto requiere un poco de trabajo extra:
Aquí está el desglose:
Entonces, si la línea actual es un duplicado de la línea 1, el registro x contendrá
d
. Al ejecutarlo se eliminará la línea actual. Si no es un duplicado, contendrá un sinsentido con el prefijo"
que, cuando se ejecuta, es un no-op, ya que"
comienza un comentario. No sé si esta es la mejor manera de lograr esto, ¡es solo la primera que se me ocurrió!Sucede que la primera línea no se puede eliminar porque el proceso de copia cambia temporalmente lo que es la línea 1. Si este no hubiera sido el caso, podría prefijarlo
:g
con un2,$
rango.Probado en Vim y ex-vi versión 4.0.
EDITAR
Y una forma más simple, que escapa de caracteres especiales para crear un patrón de búsqueda (con
'nomagic'
set), crea un:global
comando y luego lo ejecuta:Sin embargo, no puede hacer esto como una sola línea, ya que tendría un anidado
:global
, que no está permitido.fuente
Parece que la única forma POSIX de hacer esto es usar un filtro externo, como
sed
.Por ejemplo, para eliminar la línea 17 de su archivo solo si es exactamente idéntica a la línea 5 y dejarla sin cambios, puede hacer lo siguiente:
(Puede ejecutar
sed
todo el búfer aquí, o puede ejecutarlo solo en las líneas 5-17, pero en el primer caso está haciendo un filtrado innecesario, no es gran cosa, y en el último caso tendría que usar el números 1 y 13 en sused
comando en lugar de 5 y 17. Confuso).Dado que
sed
solo hace un solo pase hacia adelante, no hay una manera fácil de hacer lo contrario y eliminar la quinta línea solo si es idéntica a la 17a línea. Lo intenté por un momento como punto de curiosidad ... es complicado .Avance : puede hacerlo así:
Este es en realidad el método más general. También se puede usar para dar el mismo resultado que el primer comando (y eliminar la línea 17 solo si es idéntica a la línea 5) de la siguiente manera:
Para usos más amplios, como eliminar todas las líneas del archivo que son idénticas a la línea 37, y dejar la línea 37 intacta, puede hacer lo siguiente:
La conclusión aquí es, para verificar si dos líneas son idénticas, la mejor herramienta es
sed
noex
. Pero como DevSolar aludió en un comentario , esto no es un fracasovi
oex
están diseñados para funcionar con herramientas Unix; Esa es una gran fortaleza.fuente