¿Alguien sabe de una herramienta no basada en líneas para buscar / reemplazar cadenas "binarias" de una manera algo eficiente en la memoria? Ver esta pregunta también.
Tengo un archivo de texto de + 2GB que me gustaría procesar similar a lo que parece hacer:
sed -e 's/>\n/>/g'
Eso significa que quiero eliminar todas las nuevas líneas que ocurren después de un >
, pero no en ningún otro lugar, así que eso descarta tr -d
.
Este comando (que obtuve de la respuesta de una pregunta similar ) falla con couldn't re-allocate memory
:
sed --unbuffered ':a;N;$!ba;s/>\n/>/g'
Entonces, ¿hay otros métodos sin recurrir a C? Odio a Perl, pero estoy dispuesto a hacer una excepción en este caso :-)
No estoy seguro de ningún carácter que no aparezca en los datos, por \n
lo que me gustaría evitar el reemplazo temporal con otro carácter si es posible.
¿Alguna buena idea, alguien?
fuente
--unbuffered
?--unbuffered
$!
hacer?$!
si es así. Esto espero que necesite MUCHA memoria.sed
no es la herramienta adecuada en este caso.Respuestas:
Esto es realmente trivial en Perl, ¡no deberías odiarlo!
Explicación
-i
: edite el archivo en su lugar y cree una copia de seguridad del original llamadofile.bak
. Si no quieres una copia de seguridad, soloperl -i -pe
úsala.-pe
: lea el archivo de entrada línea por línea e imprima cada línea después de aplicar el script dado como-e
.s/>\n/>/
: la sustitución, al igual quesed
.Y aquí hay un
awk
enfoque:fuente
awk '{ORS=/>$/?"":"\n"}1'
':a;N;$!ba;s/>\n/>/g'
en su pregunta, ¡ha renunciado a su derecho a quejarse de la legibilidad! : Pfoo ? bar : baz
constructo pero no pude hacerlo funcionar.Una
perl
solución:Explicación
s///
se usa para la sustitución de cadenas.(?<=>)
es mirar hacia atrás patrón.\n
coincide con nueva línea.Todo el significado del patrón elimina todas las líneas nuevas que tienen
>
antes.fuente
s/>\n/>/
?s/>\K\n//
también funcionaríaQué tal esto:
Para GNU sed, también puede intentar agregar la opción
-u
(--unbuffered
) según la pregunta. GNU sed también está contento con esto como una simple frase:fuente
\n
si el archivo termina>\n
, pero probablemente sea preferible de todos modos.}
debe estar en una expresión separada? ¿Esto no funcionará como una expresión multilínea?b loop\n}
o-e 'b loop' -e '}'
pero no comob loop;}
y ciertamente no comob loop}
porque}
y;
son válidos en los nombres de etiqueta (aunque nadie en su sano juicio lo usaría. Y eso significa que GNU sed no es compatible con POSIX) y el}
comando necesita ser separado delb
comandosed
está contento con todo lo anterior, incluso con--posix
! El estándar también tiene lo siguiente para las expresiones de llaves -The list of sed functions shall be surrounded by braces and separated by <newline>s
. ¿Esto no significa que los puntos y comas solo deben usarse fuera de las llaves?>
. El original nunca tuvo uno, esto fue señalado por Stéphane.Debería poder usarlo
sed
con elN
comando, pero el truco consistirá en eliminar una línea del espacio del patrón cada vez que agregue otra (de modo que el espacio del patrón siempre contenga solo 2 líneas consecutivas, en lugar de intentar leer en su totalidad archivo) - intenteEDITAR: después de releer el famoso Sed One-Liners de Peteris Krumins explicado , creo que una mejor
sed
solución seríaque solo agrega la siguiente línea en el caso de que ya se haya hecho una
>
coincidencia al final, y debe condicionalmente retroceder para manejar el caso de líneas coincidentes consecutivas (es el 39 de Krumin. Agregue una línea a la siguiente si termina con una barra invertida "\" exactamente excepto por la sustitución de>
for\
como el carácter de unión, y el hecho de que el carácter de unión se retiene en la salida).fuente
>
(eso también es específico de GNU)sed
no proporciona una forma de emitir salida sin una nueva línea final. Su enfoque usandoN
fundamentalmente funciona, pero almacena líneas incompletas en la memoria y, por lo tanto, puede fallar si las líneas se vuelven demasiado largas (las implementaciones sed no suelen estar diseñadas para manejar líneas extremadamente largas).Puedes usar awk en su lugar.
Un enfoque alternativo es utilizar
tr
para intercambiar el carácter de nueva línea con un carácter "aburrido" y frecuente. El espacio podría funcionar aquí: elija un carácter que tiende a aparecer en cada línea o al menos en una gran proporción de líneas en sus datos.fuente
sed
no funciona sin un búfer de 2.5 gigabytes.tr
enfoque: mikeserv, publicaste un enfoque diferente (válido, pero menos genérico) que también se usatr
.¿Qué hay de usar ed?
(a través de http://wiki.bash-hackers.org/howto/edit-ed )
fuente
Terminé usando gsar como se describe en esta respuesta así:
fuente
Hay muchas maneras de hacer esto, y la mayoría aquí son realmente buenas, pero creo que esta es mi favorita:
O incluso:
fuente
*
. Tal como está ahora, eliminará las líneas en blanco que siguen a una línea que termina con a>
. ... mmm. Mirando hacia atrás a la pregunta, veo que es un poco ambigua. La pregunta dice: "Quiero eliminar todas las nuevas líneas que ocurren después de un>
...". Interpreto que eso significa que>\n\n\n\n\nfoo
debería cambiarse a\n\n\n\nfoo
, pero supongo quefoo
podría ser el resultado deseado.printf '>\n>\n\n>>\n>\n>>>\n>\nf\n\nff\n>\n' | tr '>\n' '\n>' | sed 's/^>*//;H;/./!d;x;y/\n>/>\n/'
- eso>>>>>>>>>>f\n\nff\n\n
me da la primera respuesta. Sin embargo, tengo curiosidad por saber qué estás haciendo para romperlo, porque me gustaría arreglarlo. En cuanto al segundo punto, no estoy de acuerdo en que sea ambiguo. El OP no solicita eliminar todo lo>
anterior a una línea\n
electrónica, sino eliminar todas las\n
líneas electrónicas que siguen a>
.>\n\n\n\n\n
, solo la primera línea nueva es después de un>
; Todos los demás siguen otras líneas nuevas. Tenga en cuenta que la sugerencia del OP "esto es lo que quiero, si solo funcionó" fuesed -e 's/>\n/>/g'
, nosed -e 's/>\n*/>/g'
.s/>\n/>/
on>\n\n\n\n\n
todavía sería algo ques/>\n/>/
se editaría.