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óncreate_empty_field(), colocando los resultados endfltspor nombre de campo. Cree un orden de campo básico, guárdelo en la matrizfld_order. Los camposfld_orderno se ponen enfld_order, pero todas las "reglas" de campo se agregan a la matrizfld_rule. - Todas las líneas serán revisadas. Compruebe el orden de los campos y solo intente imprimir los campos
fld_cntpara cualquier registro. Cualquier campo queknown_fldsrecuento de líneas enknown_fldsno se generará. - Para cualquier registro, omita los campos de
opte incrementej. - Cree una variable
fldscon el campo actual en$jo si parece que falta un campo, con un campo vacío dedflts. - Imprima
fldscon los campos adicionales vacíos pero sin campos omitidos.
Aquí hay un desglose de las funciones
create_empty_field() :
-
name, cntson argumentos del primer archivo, mientras quefld, ison variables locales configuradas en valores vacíos para usar dentro de la función. - establecer
fldparaname($1deknown_flds) - Genere tuberías hasta el valor
cnt($2desdeknown_flds).
skip_flds() :
-
fnumes el argumento para el número de campo de registro, mientras que elnamees una variable local - Extraiga la parte del
namede$fnum - Verifique si se debe omitir con la
fld_rule[name] == "skp". - Si se debe omitir, incremente
fnumy restablezca la variable dename. - Creo que las repetidas líneas
name =ysubcall 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?