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 endflts
por nombre de campo. Cree un orden de campo básico, guárdelo en la matrizfld_order
. Los camposfld_order
no 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_cnt
para cualquier registro. Cualquier campo queknown_flds
recuento de líneas enknown_flds
no se generará. - Para cualquier registro, omita los campos de
opt
e incrementej
. - Cree una variable
flds
con el campo actual en$j
o si parece que falta un campo, con un campo vacío dedflts
. - 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 quefld, i
son variables locales configuradas en valores vacíos para usar dentro de la función. - establecer
fld
paraname
($1
deknown_flds
) - Genere tuberías hasta el valor
cnt
($2
desdeknown_flds
).
skip_flds()
:
-
fnum
es el argumento para el número de campo de registro, mientras que elname
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 dename
. - Creo que las repetidas líneas
name =
ysub
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?