UNIX Shell脚本解决方案用于格式化管道分隔,分割文件(UNIX Shell Script S

2019-10-23 04:45发布

输入文件具有相同的行内多达34种不同的记录类型。

该文件是管道分隔,并且每个记录类型是由“〜”(分离除了始发记录类型。

并非所有34记录类型都包含在每一行,我不需要他们。

所有记录类型将指定的顺序内发送,但不是所有的记录类型将始终发送。 第一个记录类型是强制性的,将始终发送。 出34种,只有7是强制性的。

每个记录类型具有字段的预定数量,并且不应该从定义,而客户和我们的负载之间适当的交货时间偏差。

Oracle表将与所有基于所需的记录类型所需的列来构建。 于是一行将包含类似于输入文件中的每个记录类型的信息,但还包括其将来自未包括在输入一定的记录类型的列空。

最终的结果我要找的是为了生成可以通过SQLLDR简单地加载,而不是通过PL / SQL下去(我希望我的非shell脚本内的输出进行条件格式输入文件的方式PL / SQL同事能够解决/修复负载过程中遇到的任何问题)。

小例子有3条记录(数据类型并不在这个例子中重要):

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

1号线有没有问题,因为它拥有所有可用的记录类型,但2,3线没有。 因此,他们将需要进行修改,以包括丢失的记录类型。 整体输出就需要看是这样的:

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|||

我已经抓住每一个记录,它分割到自己的文件,并使用启动:

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 

穿上自己的行每个记录类型中所述文件,但回滚了与存在的所有可能的领域一行是相当棘手。 这显然不能,因为每个文件的最好方式可以有几千个记录,这将导致几千个文件,但我使用为出发点,以获得逻辑下来。

有什么想法吗?

Answer 1:

这里有一个可执行的awk脚本的解决方案,是不是完全严谨,但可以让你开始:

#!/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 )
}

它采用指定每种类型的字段,我已经叫“known_flds”默认设置额外的输入文件

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

具有相同分隔符的数据文件,因为我不想加入FS切换无论是在脚本或输入文件之间。 这是你的领域要求的编码。 最后部分被简写:

  • REQ - >强制性(在输入或输出或两者?)
  • 选择 - >可选(仅在输入可选)
  • SKP - >跳过(在输出)

awk.script由可执行文件,并运行像./awk.script known_flds data ,我得到下面的输出:

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||||

G在问题数据字段不出现或者具有指定的字段的正确数量或丢失输入数据的后管。

我做了至少以下假设:

  • 文件中的每个字段是正确的 - 领域本身不需要填充
  • 这些字段是按照正确的顺序,包括应该忽略的字段。
  • 任何行可能丢失可选字段,以及任何缺失,可选字段应该出现在输出中的空字段。
  • 该字段顺序可从指定known_flds文件。 否则,我可能已经选择了文件的第一行是完整的,正确的场序,以及包含用于输出所需的所有领域。 这不会允许领域被称为虽然强制性。

下面是脚本的简单故障:

  • FNR==NR -解析原始文件,并使用默认的空字段create_empty_field()函数,把结果dflts的字段名。 创建一个基本的字段顺序,将其存储在fld_order阵列。 跳过领域都放不进去fld_order ,但所有领域的“规则”添加到fld_rule阵列。
  • 所有的线将被检查。 检查领域的订单,只有试图打印出fld_cnt字段的任何记录。 在过去的行数的任何领域known_flds不会被输出。
  • 对于任何记录,跳过opt字段和增量j
  • 构建flds与无论是当前的字段变量$j ,或者如果它似乎缺少一个领域,从一个空场dflts
  • 打印出flds与另外,空字段但没有跳过领域。

下面是功能细分

create_empty_field()

  • name, cnt是从第一个文件参数,而fld, i都设置为空值,使用函数内的局部变量。
  • fldname$1known_flds
  • 生成管道长达cnt值( $2来自known_flds )。

skip_flds()

  • fnum是备案场数的参数,而name是一个局部变量
  • name从部分$fnum
  • 检查,看它是否应该被跳过fld_rule[name] == "skp"测试。
  • 如果它应该被跳过,增加fnum并重置name变量。
  • 我想重复name =sub呼线确实应该是一个新的功能,但我没有这样做,在这里。

基本上,我在做解析/变换规则known_flds ,然后训释/与执行这些awk.script对在记录data文件。 虽然这是一个合理的开始,你可以额外打印错误到另一个文件时manadatory域不存在或将是空的,缺少的子域添加到域,等你可以得到你想要的那样复杂。



文章来源: UNIX Shell Script Solution for formatting a pipe-delimited, segmented file