shell - SED 或 AWK 全部替换为另一个文件中的模式

标签 shell perl awk sed gawk

我正在尝试使用 SED 脚本进行模式替换,但它无法正常工作

sample_content.txt

288Y2RZDBPX1000000001dhana
JP2F64EI1000000002d
EU9V3IXI1000000003dfg1000000001dfdfds
XATSSSSFOO4dhanaUXIBB7TF71000000004adf
10Q1W4ZEAV18LXNPSPGRTTIDHBN1000000005egw

模式.txt
1000000001 9000000003
1000000002 2000000001
1000000003 3000000001
1000000004 4000000001
1000000005 5000000001

预期输出
288Y2RZDBPX9000000003dhana
JP2F64EI2000000001d
EU9V3IXI3000000001dfg9000000003dfdfds
XATSSSSFOO4dhanaUXIBB7TF74000000001adf
10Q1W4ZEAV18LXNPSPGRTTIDHBN5000000001egw

我可以使用单个 SED 替换,例如
sed  's/1000000001/1000000003/g' sample_content.txt

注:
  • 匹配模式不在固定位置。
  • 单行可能有多个匹配值替换sample_content.txt
  • Sample_content.txt 和 patterns.txt 有 > 100 万条记录

  • 文件附件链接: https://drive.google.com/open?id=1dVzivKMirEQU3yk9KfPM6iE7tTzVRdt_

    谁能建议如何在不影响性能的情况下实现这一目标?

    2018 年 2 月 11 日更新

    在分析了真实文件后,我得到了一个提示:有一个 等级值在 第 30 和第 31 位 .这有助于我们在何处以及所有需要应用替换的地方。
    AB级然后在 替换 10 位电话号码41-50 和 101-110
    BC级然后在 替换 10 位电话号码11-20、61-70 和 151-160
    DE级然后在 替换 10 位电话号码1-10、71-80、151-160 和 181-190

    像这样我看到 50 个独特的等级 200万样本记录。
    {   grade=substr($0,110,2)} // identify grade
    { 
        if (grade == "AB") {
            print substr($0,41,10) ORS substr($0,101,10)
        } else if(RT == "BC"){
            print substr($0,11,10) ORS substr($0,61,10) ORS substr($0,151,10) 
        }
    
        like wise 50 coiditions
    }
    

    我可以知道,这种方法是可取的还是其他更好的方法?

    最佳答案

    供日后引用的基准

    测试环境:

    使用您的示例文件 patterns.txt有 50,000 行和 contents.txt也有 50,000 行。

    来自 patterns.txt 的所有线路在所有解决方案中加载,但只有 contents.txt 的前 1000 行被审查。

    测试笔记本电脑配备双核 64 位 Intel(R) Celeron(R) CPU N3050 @ 2.16GHz,4 GB RAM,Debian 9 64 位测试,gnu sed 4.4gnu awk 4.1.4
    在所有情况下,输出都被发送到一个新文件,以避免在屏幕上打印数据的缓慢开销。

    结果:

    1. RavinderSingh13 第一个 awk 解决方案

    $ time awk 'FNR==NR{a[$1]=$2;next}   {for(i in a){match($0,i);val=substr($0,RSTART,RLENGTH);if(val){sub(val,a[i])}};print}' patterns.txt  <(head -n 1000 contents.txt) >newcontents.txt
    
    real    19m54.408s
    user    19m44.097s
    sys 0m1.981s
    

    2. EdMorton 第一个 awk 解决方案
    $ time awk 'NR==FNR{map[$1]=$2;next}{for (old in map) {gsub(old,map[old])}print}' patterns.txt <(head -n1000 contents.txt) >newcontents.txt
    
    real    20m3.420s
    user    19m16.559s
    sys 0m2.325s
    

    3.sed(我的sed)解决方案
    $ time sed -f <(printf 's/%s/%s/g\n' $(<patterns.txt)) <(head -n 1000 contents.txt) >newcontents.txt
    
    real    1m1.070s
    user    0m59.562s
    sys 0m1.443s
    

    4.赛勒斯sed解决方案
    $ time sed -f <(sed -E 's|(.*) (.*)|s/\1/\2/|g' patterns.txt) <(head -n1000 contents.txt) >newcontents.txt
    
    real    1m0.506s
    user    0m59.871s
    sys 0m1.209s
    

    5. RavinderSingh13 2nd awk 解决方案
    $ time awk 'FNR==NR{a[$1]=$2;next}{for(i in a){match($0,i);val=substr($0,RSTART,RLENGTH);if(val){sub(val,a[i]);print;next}};}1' patterns.txt  <(head -n 1000 contents.txt) >newcontents.txt
    
    real    0m25.572s
    user    0m25.204s
    sys     0m0.040s
    

    对于像 1000 行这样的少量输入数据,awk 解决方案似乎不错。
    这次让我们再做一次 9000 行的测试来比较性能


    6.RavinderSingh13 2nd awk 解决方案,9000 行
    $ time awk 'FNR==NR{a[$1]=$2;next}{for(i in a){match($0,i);val=substr($0,RSTART,RLENGTH);if(val){sub(val,a[i]);print;next}};}1' patterns.txt  <(head -9000 contents.txt) >newcontents.txt
    
    real    22m25.222s
    user    22m19.567s
    sys      0m2.091s
    

    7. 9000 行 Sed 解决方案
    $ time sed -f <(printf 's/%s/%s/g\n' $(<patterns.txt)) <(head -9000 contents.txt) >newcontents.txt
    
    real    9m7.443s
    user    9m0.552s
    sys     0m2.650s
    

    8. 9000 行并行 Seds 解决方案
    $ cat sedpar.sh
    s=$SECONDS
    sed -f <(printf 's/%s/%s/g\n' $(<patterns.txt)) <(head -3000 contents.txt) >newcontents1.txt &
    sed -f <(printf 's/%s/%s/g\n' $(<patterns.txt)) <(tail +3001 contents.txt |head -3000) >newcontents2.txt &
    sed -f <(printf 's/%s/%s/g\n' $(<patterns.txt)) <(tail +6001 contents.txt |head -3000) >newcontents3.txt &
    wait
    cat newcontents1.txt newcontents2.txt newcontents3.txt >newcontents.txt && rm -f newcontents1.txt newcontents2.txt newcontents3.txt
    echo "seconds elapsed: $(($SECONDS-$s))"
    
    $ time ./sedpar.sh
    seconds elapsed: 309
    
    real    5m16.594s
    user    9m43.331s
    sys     0m4.232s
    

    将任务拆分为更多命令(例如三个并行 sed)似乎可以加快速度。

    如果您想在自己的 PC 上重复基准测试,您可以下载文件 contents.txtpatterns.txt通过 OP 的链接或我的 github:

    contents.txt

    patterns.txt

    关于shell - SED 或 AWK 全部替换为另一个文件中的模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48719330/

    相关文章:

    linux - rsync 仅同步当天文件

    shell - 为什么在 Ubuntu 中运行 shell 脚本时出现意外标记附近的语法错误?

    linux - 使用重命名更改复杂文件名中的一个数字

    perl - 如何将 AnyEvent::Handler 与具有端口重用的套接字一起使用

    perl - 如何使用 Moose::Exporter 导出函数?

    bash - 为什么我的两个相同字符串的 bash 字符串比较总是错误的?

    ruby - 拆分字符串以仅获取前 5 个字符

    heroku - 如何使用 awk 或 sed 解析/过滤日志行

    linux - 列出Linux中配额超过指定值的用户

    regex - 对匹配的子字符串应用正则表达式