sed需要更多时间进行处理

时间:2016-05-12 02:34:12

标签: unix sed ksh

我必须在每条记录中替换某些字符(大约20种组合)。我使用sed命令实现了它。但如果文件很大(超过80000条记录),则需要超过24小时。请在下面找到代码段:

我使用了2个循环来读取输入文件并读取配置文件,其中提到了要替换的每个字符的位置。每行可以有多个需要替换的字符。当我替换字符时,我必须将其转换为十进制数字,因此需要增加下一个替换字符的位置。请在下面找到代码片段:

    ...
    #Read the input file line by line
    while read -r line
    do
 Flag='F'
   pos_count=0

   for pattern in `awk 'NR>1' $CONFIG_FILE`
   do
     field_type=`echo $pattern | cut -d"," -f6`
     if [[ $field_type = 'A' ]];then
        echo "For loop.."
        echo $pattern
        field_type=`echo $pattern | cut -d"," -f6`
        echo field_type $field_type

        start_pos=`echo $pattern | cut -d"," -f3`
        echo start_pos $start_pos
        end_pos=`echo $pattern | cut -d"," -f4`
        echo end_pos $end_pos
        field_len=`echo $pattern | cut -d"," -f5`
if [[ $Flag = 'T' && $field_type = 'A' ]];then
                if [[ $replace = 'R' ]];then
                   pos_count=$(expr $pos_count + 1)
                fi
                echo pos_count $pos_count
                val=$((2 * $pos_count))
                start_pos=$(expr $start_pos + $val)
                end_pos=$(expr $end_pos + $val)
                replace=N

        fi

        echo "$line"
        field=`expr substr "$line" $end_pos 1`
        echo  field $field
        if [[ $start_pos -gt 255 ]];then
                lim=255
                f_cnt=$(expr $start_pos - 1)
                c_cnt=$(expr $end_pos - 2)
                #c_cnt1=$(expr $c_cnt - 255)
                c_cnt1=$(expr $field_len - 2)
                f_cnt1=$(expr $f_cnt - 255)
                echo f_cnt1 "$f_cnt1" , c_cnt1 "$c_cnt1" f_cnt $f_cnt
        else
                lim=$(expr $start_pos - 1)
                f_cnt1=$(expr $field_len - 2)
                echo lim $lim, f_cnt1 $f_cnt1
        fi

        echo Flag $Flag

        case "$field_type" in
           A )
                echo Field type is Amount
                if [[ "${field}"  = "{" ]];then
                        echo "Replacing { in Amount Column"
                    replace=R
                    if [[ $start_pos -gt 255 ]];then
                       line=`echo "$line"| sed -e "s/\(.\{1,$lim\}\)\(.\{1,$f_cnt1\}\)\(.\{1,$c_cnt1\}\)\([^{]*\){/\1\2+\3.\40/"`
                    else
                       line=`echo "$line"| sed -e "s/\(.\{1,$lim\}\)\(.\{1,$f_cnt1\}\)\([^{]*\){/\1+\2.\30/"`
fi
                        Flag='T'
                elif [[ "${field}"  = "A" ]];then
                        echo "Replacing A in Amount Column"
                        replace=R
                        if [[ $start_pos -gt 255 ]];then
                                line=`echo "$line"| sed -e "s/\(.\{1,$lim\}\)\(.\{1,$f_cnt1\}\)\(.\{1,$c_cnt1\}\)\([^A]*\)A/\1\2+\3.\41/"`
                        else
                                line=`echo "$line"| sed -e "s/\(.\{1,$lim\}\)\(.\{1,$f_cnt1\}\)\([^A]*\)A/\1+\2.\31/"`
                        fi
                        Flag='T'

...
elif [[ "${field}"  = "R" ]];then
                        echo "Replacing R in Amount Column"
                        replace=R
                        if [[ $start_pos -gt 255 ]];then
                                line=`echo "$line"| sed -e "s/\(.\{1,$lim\}\)\(.\{1,$f_cnt1\}\)\(.\{1,$c_cnt1\}\)\([^R]*\)R/\1\2-\3.\49/"`
                        else
                                line=`echo "$line"| sed -e "s/\(.\{1,$lim\}\)\(.\{1,$f_cnt1\}\)\([^R]*\)R/\1-\2.\39/"`
                        fi
                        Flag='T'
                else
                        echo "Incremeting the size of Amount Column"
                        replace=R
                        if [[ $start_pos -gt 255 ]];then
                                line=`echo "$line"| sed -e "s/\(.\{1,$lim\}\)\(.\{1,$f_cnt1\}\)\(.\{1,$c_cnt1\}\)/\1\2\3  /"`
                        else
                                line=`echo "$line"| sed -e "s/\(.\{1,$lim\}\)\(.\{1,$f_cnt1\}\)/\1\2\3  /"`
                        fi
                fi
                ;;
           C )
                echo "Column Type is Count"
                ;;
           * )
                echo Others
                :;
        esac
      fi
   done
   echo "$line" >> ${RES_FILE}
done < "$SRC_FILE"
echo `date`
exit 0

以下是示例输入文件和配置文件:

CHD0000204H315604COV2013038    PROD2016022016030218481304COVCTR0000204H3156C00000000897         000000229960000024670141D0000000397577I0000000000000{00000174042
55C0000007666170B0000025070425E0000004863873E0000000631900F0000001649128{0000000018756B0000014798809C0000001890129G00000002384500000000286600000000084900000000155300000
0000055000000021388000000000048000000000003            00000897              0000000000000{0000000002706B0000001217827I000000001069

配置文件:

FIELD NO.,FIELD NAME,STARTING POSITION,ENDING POSITION,LENGTH,INDICATOR
1,CHD_CONTRACT_NO,1,5,5,N
2,CHD_FILE_ID,6,21,16,N
3,PHD_CONTRACT_NO,22,26,5,N
4,PHD_PBP_ID,27,29,3,N
5,PHD_FILE_ID,30,45,16,N
6,DET_REC_ID,46,48,3,N
7,DET_SEQ_NO,49,55,7,N
8,DET_DG_CO_ST_CD,56,56,1,N
9,DET_CURR_HICN,57,76,20,N
10,DET_LAST_SUBM_HICN,77,96,20,N
11,DET_LAST_SUBM_CH_ID,97,116,20,N
12,DET_ERL_PDE_ATT_DT,117,124,8,N
13,DET_RX_COUNT,125,135,11,N
14,DET_NET_IGD_COST_AMT,136,149,14,A
15,DET_NET_DISP_FEE,150,163,14,A
16,DET_NET_SAL_TAX_AMT,164,177,14,A
17,DET_NET_GDCB,178,191,14,A
18,DET_NET_GDCA,192,205,14,A
19,DET_NET_GRS_DG_AMT,206,219,14,A
20,DET_NET_PAT_PAY_AMT,220,233,14,A
21,DET_NET_OTR_TROOP_AMT,234,247,14,A
22,DET_NET_LICS_AMT,248,261,14,A
23,DET_NET_TROOP_AMT,262,275,14,A
24,DET_NET_PLRO_AMT,276,289,14,A
25,DET_NET_CPP_AMT,290,303,14,A
26,DET_NET_NPP_AMT,304,317,14,A
27,DET_ORIG_PDE_CNT,318,329,12,N
28,DET_ADJ_PDE_CNT,330,341,12,N
29,DET_DEL_PDE_CNT,342,353,12,N
30,DET_CAT_PDE_CNT,354,365,12,N
31,DET_ATTC_PDE_CNT,366,377,12,N
32,DET_NCAT_PDE_CNT,378,389,12,N
33,DET_NON_STD_CNT,390,401,12,N
34,DET_OON_PDE_CNT,402,413,12,N
35,DET_EST_REB_AT_POS,414,427,14,A
36,DET_VAC_ADM_FEE,428,441,14,A
37,DET_RPT_GAP_DISC,442,455,14,A
38,DET_RPT_GAP_DISC_PDES,456,467,12,N

任何人都可以建议任何其他设计方法来减少处理时间吗?

2 个答案:

答案 0 :(得分:2)

为了大幅改善性能,您需要重写它。我建议使用Python,Ruby,Awk,Perl或类似的东西。

你当前拥有灾难性能的最大原因是你的循环嵌套是错误的:

for line in data:
    for line in config:
        do stuff specified in config to line

你应该做的是:

for line in config:
    parse and store line in memory

for line in data:
    do stuff specified in config (in memory)

您可以使用上述任何语言执行此操作,我保证可以在几秒钟内处理这80,000条记录,而不是24小时。

答案 1 :(得分:1)

首先阅读注释并了解主要问题是调用外部命令的次数为80.000次。当这一切都在一个程序中完成时,开销和性能问题就解决了。哪个程序/工具由您决定。 当你坚持使用bash代码时,你将无法接近性能,但是当你尝试使用快速内部bash调用时,你可以学到很多东西。
想要改进脚本时的一些提示。

请参阅@John的回答,只读一次配置文件。

使用read分割配置文件行中的字段

while IFS="," read -r fieldno fieldname start_pos end_pos length indicator; do  
  ...   
done < configfile

避免expr
不是f_cnt1=$(expr $field_len - 2)而是(( f_cnt1 = field_len - 2))

在上次完成后重定向到outputfile,而不是每个记录(当前很难回显调试语句和结果)。

删除调试语句

使用&lt;&lt;&lt;对于字符串

当您可以更改流程时会很好,这样您就不需要调用sed(80.000条记录x 38个配置行)时间:从配置文件中生成一个可以处理所有内容的复杂sed脚本案例并且只运行一次sed -f complex.sed "$SRC_FILE" 如果要复杂,请引入字符串sed_instructions。对于每个configfile-line,将该行的sed指令添加到字符串:sed_instructions="${sed_instructions};s/\(.\{1,$lim\}\)...."。然后,您只需为每条记录拨打sed -e ''"${sed_instructions}"'' <<< ${line}一次 如果在读取$ {SRC_FILE}之前可以生成一次字符串${sed_instructions},那就太好了。

有关性能改进的另一个示例,请参阅which is the fastest way to print in awk

我认为使用bash可以将其提高到10分钟,使用awk可以提高1分钟,对@John提到的程序可以提高。