script programas programacion operaciones manejo español ejemplos comandos comando cadenas aritmeticas unix awk grep sql-loader

unix - programas - shell script linux español



Solución UNIX Shell Script para formatear un archivo segmentado delimitado por tuberías (1)

Aquí hay una solución de script awk ejecutable que no es completamente rigurosa, pero podría comenzar:

#!/usr/bin/awk -f BEGIN { FS=OFS="~" } FNR==NR { dflts[$1] = create_empty_field($1,$2) if( $3 ~ /req|opt/ ) fld_order[++fld_cnt] = $1 fld_rule[$1] = $3 next } { flds = "" j = 1 for(i=1; i<=fld_cnt; i++) { j = skip_flds( j ) if($j !~ ("^" fld_order[i])) fld = dflts[fld_order[i]] else { fld = $j; j++ } flds = flds (flds=="" ? "" : OFS) fld } print flds } function create_empty_field(name, cnt, fld, i) { fld = name for(i=1; i<=cnt; i++) { fld = fld "|" } return( fld ) } function skip_flds(fnum, name) { name = $fnum sub(//|.*$/, "", name) while(fld_rule[name] == "skp") { fnum++ name = $fnum sub(//|.*$/, "", name) } return( fnum ) }

Se necesita un archivo de entrada adicional que especifique los valores predeterminados para cada tipo de campo, al que he llamado "known_flds"

AA~5~req BB~2~opt CC~3~opt DD~6~opt EE~4~opt FF~2~skp GG~4~opt

que tiene el mismo delimitador que el archivo de datos porque no quería agregar conmutación FS en el script o entre los archivos de entrada. Es una codificación de sus requisitos de campo. El campo final es una abreviatura de:

  • req -> Obligatorio (en entrada o salida o ambos?)
  • opt -> Opcional (solo entrada opcional)
  • skp -> Skip (en salida)

Cuando awk.script se hace ejecutable y se ejecuta como ./awk.script known_flds data , obtengo el siguiente resultado:

AA|12345|ABCDE|67890|FGHIJ|~BB|12345|~CC|ABCDE|12345|~DD|A|B|C|D|E|~EE|1|2|3|~GG|F|R|T AA|23456|BCDEF|78901|GHIJK|~BB||~CC|BCDEF|23456|~DD||||||~EE|2|3|4|~GG|R|F|G AA|34567|CDEFG|89012|HIJKL|~BB||~CC|||~DD|B|C|D||~EE||||~GG||||

El campo G en los datos de las preguntas no parece tener el número correcto de campos especificados o falta un tramo final en los datos de entrada.

Hice al menos las siguientes suposiciones:

  • Cada campo en el archivo es correcto: los campos en sí no necesitan relleno
  • Los campos están en el orden correcto, incluidos los campos que se deben omitir.
  • Cualquier línea puede perder los campos opcionales, y cualquier campo opcional faltante debería aparecer como un campo vacío en la salida.
  • El orden de campo se puede designar a partir del archivo known_flds . De lo contrario, podría haber elegido la primera línea del archivo para completar, en el orden correcto de campo y contener todos los campos requeridos para la salida. Sin embargo, eso no permitiría que los campos se consideraran obligatorios.

Aquí hay un desglose simple del guión:

  • FNR==NR - analiza en el archivo original y crea campos vacíos por defecto utilizando la función create_empty_field() , colocando los resultados en dflts por nombre de campo. Cree un orden de campo básico, guárdelo en la matriz fld_order . Los campos fld_order no se ponen en fld_order , pero todas las "reglas" de campo se agregan a la matriz fld_rule .
  • Todas las líneas serán revisadas. Compruebe el orden de los campos y solo intente imprimir los campos fld_cnt para cualquier registro. Cualquier campo que known_flds recuento de líneas en known_flds no se generará.
  • Para cualquier registro, omita los campos de opt e incremente j .
  • Cree una variable flds con el campo actual en $j o si parece que falta un campo, con un campo vacío de dflts .
  • Imprima flds con los campos adicionales vacíos pero sin campos omitidos.

Aquí hay un desglose de las funciones

create_empty_field() :

  • name, cnt son argumentos del primer archivo, mientras que fld, i son variables locales configuradas en valores vacíos para usar dentro de la función.
  • establecer fld para name ( $1 de known_flds )
  • Genere tuberías hasta el valor cnt ( $2 desde known_flds ).

skip_flds() :

  • fnum es el argumento para el número de campo de registro, mientras que el name es una variable local
  • Extraiga la parte del name de $fnum
  • Verifique si se debe omitir con la fld_rule[name] == "skp" .
  • Si se debe omitir, incremente fnum y restablezca la variable de name .
  • Creo que las repetidas líneas name = y sub call deberían ser realmente una nueva función, pero no lo hice aquí.

Básicamente, estoy haciendo reglas de análisis / transformación en known_flds y luego interpretándolas / awk.script con awk.script contra registros en un archivo de data . Si bien este es un comienzo razonable, también puede imprimir errores en otro archivo cuando los campos de administración no estén presentes o estén vacíos, agregue los subcampos faltantes a los campos, etc. Puede llegar a ser tan complicado como desee.

El archivo de entrada tiene hasta 34 tipos de registros diferentes dentro de la misma línea.

El archivo está delimitado por canal, y cada tipo de registro está separado por ''~'' (excepto por el tipo de registro de origen.

No todos los 34 tipos de registros están contenidos en cada línea, y no los necesito a todos.

Todos los tipos de registros se enviarán dentro de un orden específico, pero no siempre se enviarán todos los tipos de registros. El primer tipo de registro es obligatorio y siempre será enviado. De los 34 tipos, solo hay 7 que son obligatorios.

Cada tipo de registro tiene un número predefinido de campos y nunca debe desviarse de esa definición sin el tiempo de entrega adecuado entre el cliente y nuestra carga.

La tabla de Oracle se construirá con todas las columnas requeridas en función de los tipos de registros necesarios. Por lo tanto, una fila contendrá información de cada tipo de registro similar al archivo de entrada, pero incluirá adicionalmente valores nulos para las columnas que vendrían de ciertos tipos de registros que no se incluyeron en la entrada.

El resultado final que estoy buscando es una forma de realizar un formateo condicional al archivo de entrada para generar un resultado que pueda simplemente cargarse dentro de un script de shell vía sqlldr en lugar de pasar por PL / SQL (como yo quiero que mi no- Compañeros de trabajo de PL / SQL para resolver problemas / solucionar problemas encontrados durante las cargas).

Pequeño ejemplo con 3 registros (los tipos de datos no importan en este ejemplo):

Record Types: AA, BB, CC, DD, EE, FF AA has 5 fields (Mandatory) BB has 2 fields (Optional) CC has 3 fields (Optional) DD has 6 fields (Optional) EE has 4 fields (Optional) FF has 2 fields (Not needed. Skipping in output) GG has 4 fields (Optional) AA|12345|ABCDE|67890|FGHIJ|~BB|12345|~CC|ABCDE|12345|~DD|A|B|C|D|E|~EE|1|2|3|~FF|P|~GG|F|R|T AA|23456|BCDEF|78901|GHIJK|~CC|BCDEF|23456|~EE|2|3|4|~GG|R|F|G AA|34567|CDEFG|89012|HIJKL|~DD|B|C|D||~FF|Q

La línea 1 no tiene problemas porque tiene todos los tipos de registros disponibles, pero las líneas 2 y 3 no. Por lo tanto, tendrían que modificarse para incluir los tipos de registros faltantes. El resultado general debería tener un aspecto similar al siguiente:

AA|12345|ABCDE|67890|FGHIJ|~BB|12345|~CC|ABCDE|12345|~DD|A|B|C|D|E|~EE|1|2|3|~GG|F|R|T AA|23456|BCDEF|78901|GHIJK|~BB||~CC|BCDEF|23456|~DD||||||~EE|2|3|4|~GG|R|F|G AA|34567|CDEFG|89012|HIJKL|~BB||~CC|||~DD|B|C|D||~EE||||~GG|||

Empecé tomando cada registro, dividiéndolo en su propio archivo y usando:

typeset -i count=0 while read record do newfile="`echo $file`.$count.dat" echo $record | sed ''s/|~//n/g'' > $newfile count=$count+1 done < $file

poner cada tipo de registro en su propia línea dentro de dicho archivo, pero enrollarlo nuevamente en una línea con todos los campos posibles presentes es bastante complicado. Obviamente, esta no es la mejor manera, ya que cada archivo puede tener varios miles de registros, lo que resultaría en varios miles de archivos, pero lo estaba usando como punto de partida para bajar la lógica.

¿Alguna idea?