【问题标题】:sed takes more time for processingsed 需要更多时间来处理
【发布时间】:2016-09-07 15:51:42
【问题描述】:

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

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

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

谁能建议任何其他设计方法来减少处理时间?

【问题讨论】:

  • 你不使用python、php或perl吗?
  • sed 为其任务使用有限的系统内存。如果您正在处理大文件,请尝试使用perl
  • 您运行的每个外部命令都是另一个需要生成的进程。您正在为您阅读的每一行生成 许多 命令。使用 anything 可以让您停止这样做,这将极大地帮助加快速度。此外,shell 在读取大文件方面一点也不快。因此,使用 anything else 也会大大加快速度。因此,如果您可以使用 shell 算法,请不要使用expr。不要使用echo ... | sed,可以使用sed ... &lt;&lt;&lt;"$stringvar"等。
  • @EtanReisner:因为这是ksh,我认为应该使用print 代替echo。我不确定ksh 是否支持&lt;&lt;&lt;(你知道吗?)。
  • @JohnZwinck echoprint 似乎都内置在 ksh 中,尽管 print 可能更好地指定/表现。我系统上的 ksh 手册页将 &lt;&lt;&lt; 列为可用但这种潜力是我写“在哪里可以”的原因,因为您不能总是使用它。但是您在回答中遇到了另一个主要问题。

标签: unix sed ksh


【解决方案1】:

要大幅提高性能,您需要重写此代码。我建议使用 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 小时。

【讨论】:

  • 你说这是一个严肃的答案?它将大大提高性能????那么如果他使用 awk 会怎样??
  • 他已经在使用 awk,调用它 80.000 次(对于每条记录)。读取配置的几个外部调用也完成了 80.000 次。我不相信避免这些会使 prog 快 80.000 倍,但它会快得多。
【解决方案2】:

首先阅读 cmets 并了解主要问题是调用外部命令的次数为 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))

在最后一次完成后重定向到输出文件,而不是针对每条记录(目前很困难,因为您正在回显调试语句和结果)。

删除调试语句

对字符串使用

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

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

我认为使用 bash 可以提高到 10 分钟,使用 awk 可以提高到 1 分钟,而对于@John 提到的程序则更少。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-22
    • 1970-01-01
    • 1970-01-01
    • 2017-12-04
    相关资源
    最近更新 更多