¿Cómo invertir cada 4 líneas?

13

En primer lugar, siendo esta mi primera publicación aquí, me gustaría decir que he encontrado que VIM es una gran herramienta y que el foro aquí es muy útil para encontrar respuestas a las preguntas, con mucha gente útil invaluable asistencia. Todavía soy muy nuevo en VIM, por lo que casi todo lo que aprendí sobre él proviene de aquí.

Mi pregunta es: sé cómo invertir TODAS las líneas en un archivo (: g / ^ / m0 entre otras formas), pero ¿hay alguna manera de invertir cada 4 líneas en un archivo, de modo que

line1
line2
line3
line4
line5
line6
line7
line8
...

se convierte

line4
line3
line2
line1
line8
line7
line6
line5
...

Puede suponer que siempre habrá un múltiplo exacto de 4 líneas en dichos archivos.

ablewasiereisawelba
fuente
2
Acerca de su "ps": si inserta 4 espacios delante de una línea, se interpreta como código (puede seleccionar el texto y usar los botones de formato en la parte superior). Puede encontrar más detalles sobre el icono de interrogación en la parte superior .
mMontu

Respuestas:

15

El comando :Reverseexplicado en Vim Wiki se puede usar para esto (puede incluirlo en su .vimrcpara que sea permanente):

command! -bar -range=% Reverse <line1>,<line2>g/^/m<line1>-1|nohl

Luego puede grabar una macro para ejecutar el comando en cada cuatro líneas:

qmV3j:Reverse<cr>4jq
1000@m

Explicación:

  • qm: 'q' en modo normal comienza (y detiene) la grabación de una macro en un registro dado (registro 'm', en este caso, pero podría ser cualquier otra letra)
  • V: ingrese al modo visual y seleccione la línea actual
  • 3j: expande la selección visual a las siguientes 3 líneas
  • :Reverse<cr>: ejecuta el comando Invertir en las líneas seleccionadas ( <cr>aquí se encuentra la entertecla)
  • 4j: ir a las siguientes líneas sin cambios
  • q: deja de grabar la macro
  • 1000@m: ejecute la macro grabada en el registro 'm' 1000 veces (puede aumentar este número si su archivo tiene más de 4000 líneas)

Editar:

Como se menciona en los comentarios, puede usar una macro recursiva en lugar de usar un conteo:

qmV3j:Reverse<cr>4jq
qM@mq
@m
  • qM: si el registro especificado para qes mayúscula, la macro se agrega al registro (lo que también es útil cuando se da cuenta cuando omitió los últimos pasos en una macro compleja)
  • @m: ejecuta la macro en el registro m
  • q: deja de agregar la macro

A pesar de que se crea como una macro, puede crear un comando para esto si es una tarea común en su flujo de trabajo:

function! Reverse4()
   let reg_m = @m
   let @m = '<c-r><c-r>m'
   normal! @m
   let @m = reg_m
endfunction
command! Reverse4 call Reverse4()
  • function! Reverse4()/ endfunction: define una nueva función
  • let reg_m = @m: guarda el contenido actual del registro m
  • let @m = "<c-r><c-r>m": inserte la macro en el registro m; tenga en cuenta que <c-r>es la notación vim para Ctrl+ ry que debe escribir esto, no copiar / pegar, por lo que su línea será similar let @m = 'V3j:Reverse^M4j@m'y contendrá un carácter especial (^ M)
  • normal! @m: el comando normal ejecuta su argumento como se ha escrito en modo normal, por lo que ejecutará la macro recursiva
  • let @m = reg_m: restaura el contenido del registro m, por lo que no tiene que recordar que este registro se está utilizando en esta función y evitar usarlo

  • command! Reverse4 call Reverse4(): crea un nuevo comando para esta función

Dependiendo de sus necesidades, podría mejorarlo, por ejemplo: pasar un argumento al comando y la función para que funcione para cualquier número de líneas en lugar de estar arreglado en grupos de 4 líneas.

mMontu
fuente
En lugar del 1000@qcorte, también se puede utilizar una macro recursiva: qmqqm ... @mq@m.
Pomo de la puerta
Cuando intenté ejecutar "qmV3j: Reverse <cr> 4jq", decía "E492: No es un comando de editor". Debo estar haciendo algo mal. Estoy usando, y solo he usado, GVIM, por cierto. Debería haber mencionado eso en primer lugar.
ablewasiereisawelba
Ah no importa. Lo descubrí, se supone que no debo escribir ":" antes de la parte "qmV3j: Reverse <cr> 4jq". ¡Funcionó! Muchas gracias, mMontu. Si puedo preguntar, ¿cómo incluyo el comando ": Reverse" en mi .vimrc?
ablewasiereisawelba
3
@ablewasiereisawelba Para que el comando esté disponible permanentemente, simplemente escriba la línea command! -bar -range=% Reverse <line1>,<line2>g/^/m<line1>-1|nohlen algún lugar de su vimrc.
Saginaw
@saginaw Oh, no me di cuenta de que era tan sencillo. Gracias.
ablewasiereisawelba
9

Al igual que con todas las acciones repetitivas , si puede hacer algo una vez con las operaciones básicas de edición, puede hacerlo fácilmente muchas veces grabando una macro.

(En este caso, usaré una macro recursiva, pero podría grabar una no recursiva y reproducirla varias veces martillando @@o usando un conteo).

ggqqqqqddjjpkddkkPjddpjj@qq@q

Desglosado

  1. gg: Ir al inicio del archivo.
  2. qqq: Borrar registro q. Veremos por qué esto es necesario en el paso 5.
  3. qq: Comience a grabar una macro para registrar q
  4. ddjjpkddkkPjddpjj: Una serie de operaciones que reordena las primeras cuatro líneas simplemente borrándolas y pegándolas manualmente. (Esto puede parecer complicado a primera vista, pero no se deje engañar Esto es algo muy básico que habrías aprendido en los primeros 5 minutos o así de. vimtutor: j, k, dd, p, P)
  5. @q: ¡Llama a la macro! En este punto, el registro qno contiene nada (porque lo borramos en el paso 2), por lo que no sucederá nada.
  6. q: Deja de grabar la macro.
  7. @q: Reproduce la macro recursiva tantas veces como sea necesario.

Nota: lo anterior no producirá los resultados deseados si el archivo contiene varias líneas que no son exactamente divisibles por cuatro. Para detener la macro antes de tiempo si no quedan cuatro líneas, debemos ejecutar un comando de modo normal de Vim que fallará, antes de comenzar a aplicar las ediciones en el paso 4. Podemos hacerlo intentando movernos hacia abajo una línea tres veces (y luego una copia de seguridad) antes de comenzar a mover líneas:jjj3k

Así:

ggqqqqqjjj3kddjjpkddkkPjddpjj@qq@q
Rico
fuente
¿Por qué borra explícitamente el registro q? Si solo graba en él, lo que sea que haya allí se sobrescribirá de todos modos ...
Comodín
44
@Wildcard Debido a que es una macro recursiva, la primera vez que llame al contenido del registro q, debe estar vacía, de lo contrario, arruinará sus ediciones.
saginaw
Muy agradable. Intenté esto de forma interactiva sin intentar escribir la secuencia exacta de su comando y terminé usando:qqqggqqjjjkddkkPjddjpkddkkPjjjj@qq@q
Comodín
1
@ablewasiereisawelba where I can just use the one-line custom command each time I need to do this: tenga en cuenta que no tiene que volver a crear la macro cada vez que necesita usarla, ya que es posible almacenarla como una función / comando en su vimrc.
mMontu
1
@ablewasiereisawelba ¡Me alegra que hayas encontrado útil la "Edición"! Como esta fue su primera publicación aquí, quizás no sepa cómo funcionan las notificaciones de StackExchange: cuando publica un comentario, el autor de la respuesta / preguntas recibe una notificación; otras personas recibirán una notificación solo si incluye @ <nick>. Por lo tanto, solo Rich recibió notificaciones sobre sus últimos mensajes.
mMontu
7

También puede hacer esto con un Excomando usando sedcomo filtro externo:

:%!sed -n 'h;n;G;h;n;G;h;n;G;p'

Esta versión ignorará (eliminará) cualquier línea adicional más allá de un múltiplo de 4. Para mantener el último conjunto de menos de 4 líneas (invertido), use:

:%!sed -n '$p;h;n;G;$p;h;n;G;$p;h;n;G;p'

El %aquí significa "Cada línea en el búfer".

El !comando significa "Ejecutar el siguiente comando con las líneas especificadas como entrada y reemplazar las líneas especificadas con la salida del comando". (Se llama filtro; muy útil para cosas como ordenar, por ejemplo, :%!sortordenará todas las líneas en su archivo; :2,8!sortordenará las líneas 2-8, etc.)

sedes la herramienta de edición de flujo y se encuentra en todos los sistemas tipo Unix. Los conceptos clave de los sedutilizados aquí son el "espacio de patrón" (que por defecto solo contiene cada línea de la entrada) y el "espacio de retención" (que es donde puede pegar texto adicional mientras lo usa sedpara guardarlo mientras procesa otros líneas de entrada).

-nes una opción del sedcomando para suprimir sus acciones predeterminadas de imprimir el espacio del patrón (porque en este caso solo queremos imprimir cuando lo decimos explícitamente).

$pen el sedcomando significa "Si está en la última línea de sedentrada, imprima (el espacio del patrón)".

h significa "pegar el contenido actual del 'espacio de patrón' en el 'espacio de espera', sobrescribiendo lo que esté allí".

n significa "reemplazar el contenido del 'espacio de patrón' con la siguiente línea de la entrada".

G significa "anexar al 'espacio de patrón': una nueva línea seguida por el contenido del 'espacio de retención'".

En conjunto, el sedcomando almacena cuatro líneas de salida, las invierte a medida que las almacena y luego las imprime. Los $pcomandos agregados en la segunda versión aseguran que si se alcanza la última línea del archivo que no sea en un múltiplo de 4 líneas, las líneas aún se imprimirán.


Para un enfoque alternativo e interactivo aún sin usar ninguna característica específica de Vim y también sin usar un filtro externo:

:4

para ir a la cuarta línea.

:.m -4 | +3m . | +2m . | +5

para invertir las cuatro líneas anteriores (1-4) y dejar el cursor en la línea 8.

.m -4mueve la línea actual justo después de la línea cuatro líneas hacia atrás (dejando el cursor en la línea movida).

+3m .mueve la línea que está 3 líneas después de la línea actual, justo después de la línea actual, dejando el cursor en la línea movida. +2m .por supuesto funciona igual.

+5 coloca el cursor cinco líneas hacia abajo desde donde está.

Repita como lo desee.

En Vim puede repetir todo este comando con @:, luego repetir nuevamente con @@. En POSIX vio extendría que insertarlo :.m -4 | +3m . | +2m . | +5 como una línea de texto, eliminarlo en un búfer con nombre (registro) y luego ejecutar ese búfer con nombre (registro).

Entonces, en exmodo, invierte líneas interactivamente usando solo características especificadas por POSIX, y comenzando con 17 líneas de texto:

Entering Ex mode.  Type "visual" to go to Normal mode.
:0a     # Append following text after "line 0" (i.e. insert at start of file).
.m -4 | +3m . | +2m . | +5
.       # End text insertion
:d k    # Delete that line to register k
line1   # This is a printout of the current line
:4      # Move to line 4
line4
:@k     # Execute register k to reverse lines 1-4
line8
:@@     # Execute register k again
line12
:@@     # Execute register k again
line16
:@@     # Execute register k again
line17
:%p     # Print the whole buffer (just to see what was done)
line4
line3
line2
line1
line8
line7
line6
line5
line12
line11
line10
line9
line16
line15
line14
line13
line17
:wq     # Save and quit

Otras lecturas:

Comodín
fuente
¿Podría explicar un poco más su solución?
vappolinario
3
@vappolinario, agregué más explicaciones. ¿Eso ayuda? :)
Comodín
Wow, respuesta muy larga. :) He sed, en realidad, pero lo he usado con un script simple que encontré en algún lugar, probablemente en este tablero, que fue una solución a un problema que tenía. Sin embargo, cuando traté de ejecutar su línea sugerida, dice "'sed' no se reconoce como un comando interno o externo, un programa operable o un archivo por lotes". No estoy seguro si necesito tener sed en la carpeta vim o qué.
ablewasiereisawelba
1
@ablewasiereisawelba, si está ejecutando en un cuadro de Windows y no está utilizando Cygwin (o MobaXterm o similar), puede probar el otro método que le di: 4G:.m -4 | +3m . | +2m . | +5<Enter>@:luego @@repita hasta que su archivo esté completamente procesado. Sin embargo, recomiendo instalar MobaXterm. :)
Comodín el
@Wildcard Oh, vale, estoy revisando MobaXterm ahora. :)
ablewasiereisawelba
7
:g/^/exe 'm .-' . substitute(line('.') % 4, '^0$', '4', '')

Inglés simple: para cada línea, mueva la línea actual hacia arriba lnum % 4, a menos que lnum % 4 == 0, en cuyo caso, mueva la línea actual hacia arriba 4.

Además, para invertir todas las nlíneas, reemplace las 4's en el comando anterior con n.

djjcast
fuente
2
Muy buen comando de una línea. Gracias, djjcast.
ablewasiereisawelba