¿Cómo extraer bytes del medio de un archivo?

1

Estamos analizando algunos archivos EDI grandes que no contienen CR / LF. Sin embargo, tienen ~ ( tilde ) como un delimitador de segmento.

Estoy tratando de extraer el registro de control para el archivo y los últimos bytes de mi archivo de 120 MB se parecen a esto:

~REF*1L*0711882~SE*62300*39093~GE*1*500001242~IEA*1*500001241~

Solo hay un registro de control en el archivo y siempre comienza con ~SE.

Entonces, ¿hay una manera fácil de usar Unix estándar cortar , awk , grep , etc. herramientas para cortar este archivo para obtener el segmento SE * 62300 * 39093, aparte de convertir el ~ a CRLF y siguiendo las últimas tres líneas del archivo?

Renuncia:
No soy un gurú de Unix, por lo que la respuesta puede ser obvia para un usuario experimentado. Además, no tengo control sobre el formato de archivo.

Noah
fuente
¿Qué hay de malo en convertir el ~ a nuevas líneas y siguiendo las últimas 3 líneas del archivo. Si se sabe que el archivo no contiene nuevas líneas, esto no introduce ninguna ambigüedad en el formato y, francamente, es la mejor forma de incluir el archivo en un formato que facilite el trabajo de todas las herramientas basadas en líneas.
Celada
@Celada: No soy una persona de Unix, pero convertir cientos de megabytes para extraer los últimos 100 o más personajes parece una exageración; Algunos de estos archivos pueden ser muy grandes, y estoy buscando la forma más fácil de hacerlo.
Noah
Puede filtrar hasta las últimas líneas de un archivo usando tail. No hay necesidad de analizarlo todo. Algo como tail edi_file | grep ~SE | cut -d'~' -f 3 (donde edi_file es el nombre de su archivo grande) (Descargo de responsabilidad: el ejemplo solo funciona si el campo requerido está en el campo # 3 (delimitado por ~ 's como por -d ~. Eso podría necesitar un ajuste. ¿Podemos obtener un ejemplo más grande del archivo de entrada?
Hennes
120MB no es tan grande. Nadie se ha preocupado de exprimir cada bit de rendimiento de un script de shell. Si quieres eso, usa C :-) Así que la respuesta de Michael Kohne es más o menos lo que haría. O si el archivo realmente es demasiado grande para que usted quiera leerlo todo, prefiltre con algo como tail --bytes=5000 ding... y luego esperas que los últimos 5000 bytes sean suficientes para abarcar las 3 líneas que necesitas.
Celada
Para una cosa fuera de tiempo. Estoy de acuerdo. Dejalo correr. Para algo usado diariamente me gusta solo para analizar la cola. Tanto porque no es un desperdicio y porque se siente mal desperdiciar innecesariamente. (No es que intentar encontrar una respuesta durante 20 minutos no sea un desperdicio. Sin embargo, no hay opción de bytes en BSD).
Hennes

Respuestas:

3

Puedes hacer esto con:
tr "~" "\n" < edi_file | tail -20 | grep ^SE

El tr tr Anuncia todos los tildes a nuevas líneas. (Los que están representados por un \ n).

La salida que luego envió a la cola, que descarta todas, excepto las últimas 20 líneas.

Probablemente pueda ajustar esto, dependiendo de lo que quiera buscar. Sin él, todo el archivo se alimenta a grep, que probablemente requiere muchos más recursos que la cola. Si tiene una versión específica de tail que admite mostrar parte de un archivo basado en bytes en lugar de líneas, incluso puede utilizar este paso antes.

No elegí esa opción porque tu publicación está etiquetada como genérica Unix más bien que Linux moderno con herramientas GNU actualizadas y extensiones específicas de GNU .

Finalmente grep Filtra las líneas finales a las que contienen SE, y la carretilla ( ^ ) se asegura de que esté al principio en una línea. (Previniendo cosas como ~ foooo SE foobarquz ~ SE Queremos mostrar ~ boobar ~ para mostrar dos líneas).

Hennes
fuente
4

Si bien puedo ver que no quiero modificar el archivo original, puedes hacer la traducción en una canalización. De esa manera, no está modificando los datos, pero aún así obtiene la ventaja (en términos de utilidad de Unix) de convertir ~ en fin de línea.

Esto debería funcionar:

cat ding | tr "~" "\n" | tail -3

No es la cosa más eficiente del universo, pero incluso en un archivo de 120 MB no debería ser un gran problema para ejecutar.

Tenga en cuenta que las citas en los dos conjuntos no son opcionales, ambos ~ y \n será interpretado por el shell si sueltas las comillas.

Michael Kohne
fuente
3
tr "~" "\n" < edi_file | tail -20 | grep ^SE ? (No hay necesidad de usar cat cuando la entrada puede ser redirigida Grep para mostrar solo los campos que comienzan con SE.
Hennes
@Hennes: Esta es una respuesta más simple, puede agregarla y la aceptaré. Lo que terminé usando fue tr "~" "\ n" & lt; edi_file | cola -3 | head -n 1 Sin embargo, esto es solo porque sé que SE es siempre el tercer al último segmento
Noah
Hecho. Conocer su formato de datos específico ayuda. Agregué algunas explicaciones más al post siguiente y a la razón por la que lo usé.
Hennes
2

Será ineficiente en archivos grandes tr Primero, porque realmente quieres datos del final, y tr Procesará los datos que serán descartados.

Utilizar tac para leer el archivo a la inversa, luego tome las 20 primeras líneas (de la inversa, así que en realidad la última), invierta nuevamente para obtener el pedido original, ahora grep:

tac -s~ edi_file | head -n 20 | tac | grep ^SE

Recuerda que no puedes seek() ¡un tubo!

Janus Troelsen
fuente
1
Querrá citar los ~ caracteres, dependiendo del shell, un solo ~ puede expandirse en algo.
Michael Kohne
@MichaelKohne: Sí. Pero parece que tac se convertirá en nuevas líneas, por lo que tr no debería ser necesario
Janus Troelsen
@ysangkok: Es posible que haya perdido el punto de que solo hay 1 línea en el archivo.
Noah
@Noah: Es por eso que uso el -s bandera para tac
Janus Troelsen
@ysangkok: No etiqueté la pregunta solaris porque pensé que no importaría. Pero parece que tac no está soportado bajo solaris. Actualicé tu respuesta porque aprendí algo nuevo y parece que hubiera funcionado en otros sistemas * nx
Noah