
Tengo un archivo como este:
Dir1/File1.cpp Dir2/File2.cpp \
Dir3/File1.h Dir4/File2.cpp \
Dir2/File1.cpp \
Dir2/File1.h \
Quiero generar un archivo como este:
Dir1/File1.cpp
Dir2/File2.cpp
Dir3/File1.h
Dir4/File2.cpp
Dir2/File1.cpp
Dir2/File1.h
¿Cómo hacer esto usando Bash/Sed/Awk/Grep o algo como esto?
Respuesta1
Si tienes un Awk que soporta expresiones regulares para el separador de registros RS
, se puede hacer así:
awk 'BEGIN { RS = " +| *\\\\?\\n" } 1'
La ventaja de esto es que no estamos guardando todo el archivo en la memoria y reemplazando algunas expresiones regulares; su entrada podría tener una longitud de gigabytes.
Básicamente, tratamos el archivo como si tuviera dos separadores de registros: uno o más espacios, o cero o más espacios seguidos de una nueva línea, que puede ir precedida por una barra invertida opcional.
Habiendo delimitado los registros de esta manera, todo lo que tenemos que hacer es generarlos seguidos del separador de registros de salida predeterminado ( ORS
), que, por supuesto, es una nueva línea. Esto se logra mediante una regla de patrón de acción que consiste en 1
.
O un trabajo de canalización con sed
y tr
, sin usar nada que no esté en POSIX:
tr '\n' ' ' | sed -e 's/\\//g' -e 's/ \+/ /g' | tr ' ' '\n'
Reemplace las nuevas líneas con espacios. Luego, aplaste tramos de múltiples espacios en un espacio mientras elimina las barras invertidas. Luego asigne espacios a nuevas líneas.
Respuesta2
Con GNUgrep
$ cat file
Dir1/File1.cpp Dir2/File2.cpp \
Dir3/File1.h Dir4/File2.cpp \
Dir2/File1.cpp \
Dir2/File1.h \
$ grep -o '[^\ ]*' file
Dir1/File1.cpp
Dir2/File2.cpp
Dir3/File1.h
Dir4/File2.cpp
Dir2/File1.cpp
Dir2/File1.h
-o
extraer solo el patrón coincidente[^\ ]*
cero o más caracteres que no sean espacios ni\
caracteres, ya que*
es codicioso, intentará hacer coincidir tantos caracteres como sea posible
para guardar el resultado en otro archivo, utilice
$ grep -o '[^\ ]*' file > out_file
Como señaló @Stéphane Chazelas, es mejor usar lo siguiente por ser más portátil:
grep -oE '[^\ ]+' file
donde -E
invoca expresiones regulares extendidas y [^\ ]+
coincide con uno o más caracteres que no son espacios ni \
caracteres
Análisis de rendimiento:
$ perl -ne 'print "$_"x100000' file > file_big
$ shuf file_big -o file_big
$ du -sh file_big
9.0M file_big
Todas las respuestas y sugerencias de los comentarios utilizados para la comparación:
$ time grep -o '[^\ ]*' file_big > o1
real 0m2.090s
user 0m2.076s
sys 0m0.016s
$ time grep -oE '[^\ ]+' file_big > o2
real 0m1.523s
user 0m1.504s
sys 0m0.012s
$ time awk 'BEGIN { RS = " +| *\\\\?\\n" } 1' file_big > o3
real 0m0.331s
user 0m0.320s
sys 0m0.008s
$ time tr -s '\\ ' '[\n*]' < file_big | grep . > o4
real 0m0.095s
user 0m0.124s
sys 0m0.008s
$ time tr '\\ ' '[\n*]' < file_big | grep . > o5
real 0m0.105s
user 0m0.104s
sys 0m0.016s
Prueba de cordura
$ diff -s o1 o2
Files o1 and o2 are identical
$ diff -s o1 o3
Files o1 and o3 are identical
$ diff -s o1 o4
Files o1 and o4 are identical
$ diff -s o1 o5
Files o1 and o5 are identical