global-command – ¿Cómo unir todas las líneas que patrón coincidente?

Pregunta:

Me gustaría unir líneas solo para líneas que tienen cierto patrón (como ; ), sin embargo, cuando se usa g/;/j , no funciona como se esperaba a menos que se llame un par de veces.

Por ejemplo, el siguiente contenido:

a
1;
2;
3;
4;
5;
b
6;
7;
8;
9;
c

cuando se usa:: :g/;/j la salida es:

a
1; 2;
3; 4;
5; b
6; 7;
8; 9;
c

o :g/;/-j da:

a 1; 2; 3; 4; 5;
b 6; 7; 8; 9;
c

similar con:: :g/;\_.\{-};/j .

Mi resultado esperado es:

a 
1; 2; 3; 4; 5;
b
6; 7; 8; 9;
c

o algo similar, por lo que todas las líneas que contienen el patrón se unen.

¿Cómo se puede lograr esto?

Respuesta:

Posible explicación del problema.

Creo que la razón por la que :g/;/j no funciona es porque el comando :g opera con un algoritmo de 2 pasos:

  • durante la primera pasada marca las líneas que contienen el patrón ;
  • durante la segunda pasada opera en las líneas marcadas

Durante la segunda pasada,: :g une a la línea 1; con la línea 2; porque 1; fue marcado durante la primera pasada. Sin embargo, sospecho (no estoy seguro) que no se une a 1; 2; con 3; porque la línea 2; ya no existe, su contenido se ha fusionado con la línea 1; que ya ha sido procesado.

Entonces :g busca la siguiente línea que se marcó durante la primera pasada ( 3; ) y la une con la siguiente ( 4; ). Después de eso, el problema se repite, no puede unirse a 3; 4; con 5; porque la línea 4; ya no existe.

Solución 1 (con vimscript)

Quizás pueda llamar a una función siempre que una línea contenga ; se encuentra para comprobar si la línea anterior también contiene un punto y coma:

function! JoinLines()
    if getline(line('.')-1) =~ ';'
        .-1join
    endif
endfunction

Luego use el siguiente comando global:

:g/;/call JoinLines()

O sin función:

:g/;/if getline(line('.')-1) =~ ';' | -j | endif

Solución 2 (sin vimscript)

:g/;/.,/^[^;]*$/-1j

Siempre que el comando global :g encuentre el patrón ; ejecuta el comando: .,/^[^;]*$/-1j

Se puede desglosar así:

:g/pattern/a,bj

Donde :

pattern = ;
a       = .           = number of current line
b       = /^[^;]*$/-1 = number of next line without any semicolon minus one

b se puede desglosar aún más así:

/    = look for the number of the next line matching the following pattern
^    = a beginning of line
[^;] = then any character except a semicolon
 *   = the last character can be repeated 0 or more times
 $   = an end of line
 /   = end of pattern
 -1  = removes one to the number you just got

j es la forma abreviada del comando Ex :join que, como la mayoría de los otros comandos Ex, puede ir precedido de un rango.
Aquí está precedido por el rango: .,/^[^;]*$/-1 ( a,b )
Una gama sigue la forma a,b , donde a y b son generalmente 2 números de línea, y le permite operar en un grupo de líneas cuyo número está comprendido entre a y b , en lugar de sólo uno.

Entonces, el comando j une todas las líneas entre la actual ( a ) y la siguiente que no contiene ningún punto y coma menos uno ( b ).

Para más información, ver:

:help :global
:help :join
:help :range

Leave a Comment

Your email address will not be published. Required fields are marked *

web tasarım