¿Es posible con Gedit o la línea de comando modificar cada cuarta línea de un archivo de texto?

11

Estoy tratando de convertir un archivo de texto en una hoja de cálculo separada por pestañas. Mi archivo de texto es algo como esto:

Dog
Cat
Fish
Lizard
Wolf
Lion
Shark
Gecko
Coyote
Puma
Eel
Iguana

Con las funciones estándar de búsqueda y reemplazo en Gedit o LibreOffice, es fácil reemplazar el final de línea con una pestaña. Pero si solo cambio los retornos de carro por pestañas, obtendré esto:

Dog   Cat   Fish   Lizard   Wolf   Lion   Shark   Gecko   Coyote   Puma   Eel   Iguana

Pero lo que tengo que hacer es que se vea así:

Dog   Cat   Fish   Lizard
Wolf   Lion   Shark   Gecko  
Coyote   Puma   Eel   Iguana

Entonces, ¿puedo cambiar cada carácter de final de línea por una pestaña, excepto por cada cuarta línea?

No sé si ese tipo de iteración condicional se puede hacer con expresiones regulares dentro de un programa como Gedit o LibreOffice, ¿entonces tal vez esto deba ser algún tipo de función de línea de comando? Ni siquiera tengo claro cuál es la mejor herramienta para comenzar.


Actualizar:

Intenté los siguientes comandos:

sed 'N;N;N;s/\n/\t/g' file > file.tsv

paste - - - - < file > file.tsv

pr -aT -s$'\t' -4 file > file.tsv

xargs -d '\n' -n4 < inputfile.txt

Pero cuando trato de abrir el tsvarchivo resultante en LibreOffice, las columnas no están del todo bien. No estoy seguro de si esto significa que no estoy ejecutando los comandos anteriores correctamente, o si estoy haciendo algo mal en la función de importación de LibreOffice:

Apertura de TSV en Calc

Solo como referencia, el resultado deseado debería verse así:

Columnas adecuadas

Preguntador
fuente

Respuestas:

16

Usted podría utilizar un editor de línea de comandos comosed

sed 'N;N;N;s/\n/\t/g' file > file.tsv

o, más programáticamente, agregando caracteres de continuación de línea de barra diagonal inversa a cada una de las líneas que desea unir usando el n skip moperador de dirección de GNU sed y siguiéndola con el clásico un trazo para unir líneas continuas:

sed '0~4! s/$/\t\\/' file | sed -e :a -e '/\\$/N; s/\\\n//; ta'

Ver por ejemplo Sed One-Liners Explicado :

  1. Agregue una línea a la siguiente si termina con una barra invertida "\".

    sed -e :a -e '/\\$/N; s/\\\n//; ta'
    

Sin embargo, en mi humilde opinión sería más fácil con una de las otras utilidades estándar de procesamiento de texto, por ejemplo

paste - - - - < file > file.tsv

(el número de -corresponderá al número de columnas) o

pr -aT -s$'\t' -4 file > file.tsv

(puede omitir el -s$'\tsi no le importa que la salida esté separada por varias pestañas).


El extraño comportamiento de reimportación que está observando es casi seguro porque el archivo original tiene terminaciones de línea CRLF estilo Windows. Si necesita trabajar con archivos de Windows, puede transferir la conversión al comando de varias maneras, p. Ej.

tr -d '\r' < file.csv | paste - - - -

o

sed 'N;N;N;s/\r\n/\t/g' file.csv

El primero eliminará TODOS los retornos de carro, mientras que el último conservará un CR al final de cada una de las nuevas líneas (que puede ser lo que desee si el usuario final previsto está en Windows).

conductor de acero
fuente
1
Una nota sobre los finales de línea de estilo Windows: las herramientas estándar para convertir entre ellos y estilo Unix son dos2unixy unix2dos.
David Foerster
13

Puede usar xargspara agrupar siempre cuatro líneas en una, separadas con un solo espacio cada una:

xargs -d '\n' -n4 < inputfile.txt

-d '\n'establece el delimitador de entrada en un carácter de nueva línea, de lo contrario, también se rompería en espacios. Si de todos modos solo tiene una palabra por línea de entrada, incluso puede omitir esto.
-n4establece el número de argumento (el número de elementos de entrada por línea de salida) en 4.

Salida:

Dog Cat Fish Lizard
Wolf Lion Shark Gecko
Coyote Puma Eel Iguana

O si desea pestañas como separadores en lugar de un espacio, puede reemplazarlos después. Sin embargo, si tuviera espacios en sus líneas de entrada, también se reemplazarían:

xargs -d '\n' -n4 | tr ' ' '\t'

Salida (mirar dependiendo del ancho de la pestaña del navegador / terminal):

Dog Cat Fish    Lizard
Wolf    Lion    Shark   Gecko
Coyote  Puma    Eel Iguana
Byte Commander
fuente
Este método tiene la ventaja de que se comporta razonablemente incluso cuando el número total de líneas de entrada no es un múltiplo de cuatro.
Eliah Kagan
3

También puedes usar:

awk -v ORS="" '{print $1; print NR%4==0?"\n":"\t"}' file > file.tsv 

Las dos variables incorporadas awk son:

  • ORS: O utput R ECORD S eparator (por defecto = salto de línea). Se agrega al final de cada comando de impresión.
  • NR: N umber de la corriente R ow awk está procesando.

Este comando, para cada línea, mostrará el contenido de la primera columna (y aquí solo). Luego elige agregar una nueva línea o una pestaña probando el resto de la división de NRpor 4.

arauk
fuente
3

Otro awkenfoque más corto :

awk '{printf $0 (NR%4?"\t":"\n")}' infile

Este printf sólo una columna seguida por el siguiente y el siguiente y ... y una lengüeta de \tcarácter después cada uno, pero será printf un \ncarácter ewline cuando N úmero de R ECORD era el factor de 4 (en la que NR%4devolverá 0 (falso), que es lo ternario operador condition(s)?when-true:when-falseestá haciendo.)

αғsнιη
fuente
3

Mi solución a esto sería usar la combinación de sedy sed. Primero, puede marcar cada cuarta línea con algún carácter especial, por ejemplo >, usando esta solución:

En este caso, desea comenzar desde la línea 5 y marcar cada 4ta línea después de ella. En GNU sedeso se puede dar como una dirección 5~4. Puedes usar este comando:

sed '5~4s/^/>/' file1 > file2

Luego debe eliminar las nuevas líneas, lo que se puede hacer con un sedbucle:

sed ':a;N;s/\n/ /;ba' file2 > file3

Hay formas más fáciles de convertir nuevas líneas a algún otro personaje, por ejemplo con tr:

tr '\n' ' ' < file2 > file3

De cualquier manera, combinando los dos da

Dog   Cat   Fish   Lizard   >Wolf   Lion   Shark   Gecko   >Coyote   Puma   Eel   Iguana

(la sedversión deja una nueva línea final, mientras que la trversión no)

Después de eso, solo necesita convertir los caracteres especiales que insertó en nuevas líneas; consulte, por ejemplo, Convertir un archivo delimitado por tabuladores para usar líneas nuevas . En este caso, cambie >a nuevas líneas:

sed 'y/>/\n/' file3 > outfile

El ycomando realiza la misma función que tr, transformando un personaje en otro, pero puede usar el scomando aquí igualmente bien. Con s, debe goperar en cada partido en la línea ( sed 's/>/\n/g').

En lugar de hacer dos archivos intermedios, puede usar tuberías:

$ sed '5~4s/^/>/' file | sed ':a;N;s/\n/ /;ba' | sed 'y/>/\n/'
Dog Cat Fish Lizard 
Wolf Lion Shark Gecko 
Coyote Puma Eel Iguana

Si los espacios finales son un problema, puede agregar otro comando para eliminarlos:

| sed 's/ $//'
spaceman117X
fuente
2

En aras de la "integridad" aquí hay una solución de bash puro:

#!/usr/bin/env bash

sep=$'\t'

while read one \
      && read two \
      && read three \
      && read four
do
  printf "%s\n" "$one$sep$two$sep$three$sep$four"
done

Funciona también con espacios, suponiendo que IFSesté configurado correctamente (que debería, por defecto, AFAIK). Además, creo que esto podría incluso ser un script de shell portátil y funcionar con cualquier shell compatible con POSIX.

Daniel Jour
fuente
1
Esto no es portátil para shells compatibles con POSIX en general, porque $' 'POSIX no requiere la forma de cotización. Por ejemplo, en dash(que proporciona shpor defecto en Ubuntu), ejecutando printf '%s\n' $'a\tb'solo salidas $a\tb. Sin embargo, eso no significa que esto no sea útil; funciona en bash. Sin embargo, como con algunas de las otras soluciones que la gente ha publicado, produce resultados incompletos si el número de líneas de entrada no es múltiplo de cuatro. Además, recomiendo usar read -r, ya que no hay razón para pensar que la expansión de las barras invertidas en el archivo de entrada se desee aquí.
Eliah Kagan
Simplemente podría hacerloprintf '%s\t%s\t%s\t%s\n' "$one" "$two" "$three" "$four"
terdon
2

Una macro vim (grabada con q) podría aplicar su operación, luego omitir tres líneas. Luego, solo ejecuta esa macro n veces.

p.ej:

qq $ J i <TAB> <ESC> $ J i <TAB> <ESC> $ J i <TAB> <ESC> ^^ j qq 100 @q
rackandboneman
fuente
2

Como solicitó una solución de Gedit, algo como esto debería funcionar:

Encontrar:

(\w+)[\r\n]+(\w+)[\r\n]+(\w+)[\r\n]+(\w+)[\r\n]+

Reemplazar con:

\1\t\2\t\3\t\4\n

Asegúrese de que la casilla de verificación para las expresiones regulares esté marcada.

Cómo funciona:

El primer paso es encontrar una serie de caracteres de palabras, con \ w +, y capturar los resultados en la variable \ 1 colocando paréntesis alrededor de la expresión:

(\w+)

A continuación buscamos una serie de caracteres finales de línea, \ r y \ n, o CR y LF. Dado que los archivos con formato de Windows usan ambos, creamos una clase de caracteres envolviendo estos dos caracteres entre corchetes. La ventaja hace que busque uno o más caracteres:

[\r\n]+

Finalmente, repetimos esto 3 veces más, almacenando cada palabra subsiguiente en las variables \ 2, \ 3 y \ 4. Esto hace que nuestro reemplazo con expresión sea simple. Solo necesitamos colocar los caracteres de tabulación, \ t, y un nuevo carácter de línea, \ n, en los lugares apropiados para el formato que necesita.

Jason Wood
fuente