V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
shuizhongyu10
V2EX  ›  Linux

问个 shell 命令文件行提取问题

  •  
  •   shuizhongyu10 · 2019-03-08 10:49:40 +08:00 · 3235 次点击
    这是一个创建于 2090 天前的主题,其中的信息可能已经有所发展或是发生改变。
    两个文件 A,B,A 单列文件,B 多列文件
    对 A 中每一行,到 B 中找对应第一列相同的行,提取出来到 C 文件中。
    目前我是这样写的:
    cat A | awk '{printf("grep %s B>>C\n",$1)}' | bash
    但无奈实在有点慢,这里的 B 文件比较大,300w+行,A 文件 10w+行。
    老哥们有什么方案能快点的吗?
    第 1 条附言  ·  2019-03-08 13:26:51 +08:00
    A B 文件我放在网盘上了,大家如果有兴趣可以试试
    链接: https://pan.baidu.com/s/1iWWhjhEEpUtMErDCC3Q3kA 提取码: xxr5
    第 2 条附言  ·  2019-03-08 13:29:00 +08:00
    目前采用 @Sherlocker 老哥的方法,速度很快。
    awk 'NR==FNR{a[$1]=$0;next}NR>FNR{if($1 in a)print $0}' a b > c
    26 条回复    2019-04-15 11:23:01 +08:00
    necomancer
        1
    necomancer  
       2019-03-08 11:38:51 +08:00   ❤️ 1
    你可能需要 look 命令:
    man look:
    look - display lines beginning with a given string
    建议你看看手册。另外,先排序 B 文件会提高很多效率:
    sort -d B > Bsorted
    while read line
    do
    look ${line} Bsorted >> C
    done < A
    希望你的 A,B 都是 unique 的,重复内容浪费资源。
    至于 sort 和 uniq,都有比较快的方法,比如 linux 下还能用 parallel 加速。
    necomancer
        2
    necomancer  
       2019-03-08 11:48:52 +08:00
    或者使用 sgrep,我记得 sgrep 是需要排序好的文件的,用二分查找。
    necomancer
        3
    necomancer  
       2019-03-08 11:50:22 +08:00
    look 也是 binary search,但只能查行头。看实际需求吧。
    geelaw
        4
    geelaw  
       2019-03-08 11:56:29 +08:00
    因为你每次都要重新读一次 B 文件 - - 先存到内存里比较好,所以请换 PowerShell 或者脚本语言。

    $b = {} # 大小写不敏感,要得到敏感版本,使用 [System.Collections.Generic.Dictionary[string,string]]::new()
    gc B | % { $b[$_.Substring(0, $_.IndexOf(' ') - 1)] = $_ }
    gc A | % { $b[$_] } | sc C
    hugee
        5
    hugee  
       2019-03-08 11:59:39 +08:00
    @shuizhongyu10 建议把文本发出来,这样我们好测试代码执行速度。
    necomancer
        6
    necomancer  
       2019-03-08 12:05:43 +08:00
    每次读文件是个问题,sort -d B > /tmp/Bsorted,如果可以放内存里的话^_^
    Sherlocker
        7
    Sherlocker  
       2019-03-08 12:26:27 +08:00   ❤️ 2
    给个文本截图啊,如果分隔符都是空格应该一条 awk 命令搞定
    awk 'NR==FNR{a[$1]=$0;next}NR>FNR{if($1 in a)print $0}' a b > c
    shuizhongyu10
        8
    shuizhongyu10  
    OP
       2019-03-08 13:05:27 +08:00
    @necomancer 感谢老哥 ,试了下你这种,速度差别不是很大 。最大瓶颈应该就是重复读入了 B 文件,我就是不知道怎么只读入一次
    shuizhongyu10
        9
    shuizhongyu10  
    OP
       2019-03-08 13:06:45 +08:00
    @geelaw 老哥。你这是个什么语言,我都不会用
    shuizhongyu10
        10
    shuizhongyu10  
    OP
       2019-03-08 13:08:17 +08:00
    牛逼了老哥,你这个速度贼快。你这种应该就是解决了多次读入 B 文件的问题,nb !
    shuizhongyu10
        11
    shuizhongyu10  
    OP
       2019-03-08 13:13:18 +08:00
    @Sherlocker 牛逼了老哥,你这个完美解决了多次读 B 的问题,A,B 文件都只读一次,速度贼快。
    shuizhongyu10
        12
    shuizhongyu10  
    OP
       2019-03-08 13:13:49 +08:00
    @hugee 那我一会传网盘,放个链接?
    zbinlin
        13
    zbinlin  
       2019-03-08 13:42:33 +08:00   ❤️ 1
    awk 'BEGIN { while ( (getline line<"A") > 0 ) { hash[line] = 1 } } hash[$1] == 1 { print $0 }' B
    mononite
        14
    mononite  
       2019-03-08 13:56:06 +08:00
    A 和 B 都读取一次,先读 A 保存起来,再读 B 过滤,用 awk 处理很直接。
    awk 'NR==FNR{a[$1];next}$1 in a' A B
    geelaw
        15
    geelaw  
       2019-03-08 14:28:26 +08:00   ❤️ 1
    @geelaw #4 这里应该改成 $b=@{} 然后 gc 需要调优,正确的代码

    measure-command {
    $sz = gc b -read 16000 | measure | select -expand count
    $sz *= 2 * 16000
    $b = [System.Collections.Generic.Dictionary[string,string]]::new($sz)
    gc b -read 16000 | % { foreach ($x in $_) {
    $b[$x.substring(0,$x.indexof(' '))] = $x
    } }
    gc a -read 16000 | % { foreach ($x in $_) { $b[$_] } } | sc C -encoding utf8
    }

    需要 2 min 左右。

    不过更好的方法是把 a 存进内存,然后 stream 处理 b,如下

    measure-command {
    $s = [System.Collections.Generic.HashSet[string]]::new([string[]](gc a))
    gc b -read 16000 | % { foreach ($x in $_) { if ($s.contains($x.substring(0,$x.indexof(' ')))) { $x } } } | sc C -encoding utf8
    }

    只要 12.5s 。

    @shuizhongyu10 #9 答案在 #4 第一行
    hanxiV2EX
        16
    hanxiV2EX  
       2019-03-08 14:34:29 +08:00 via Android
    我只会 sort 之后,再 diff。awk 还是不够熟练。
    rrfeng
        17
    rrfeng  
       2019-03-08 14:34:32 +08:00 via Android
    不是 join 吗?
    shuizhongyu10
        18
    shuizhongyu10  
    OP
       2019-03-08 14:40:32 +08:00
    @geelaw 感谢感谢 PowerShell 有机会的话去学一下,目前采用的 awk 方法完全满足了需求。
    测了下时间,只要 2.3s ,哈哈。
    necomancer
        19
    necomancer  
       2019-03-08 16:48:11 +08:00
    @shuizhongyu10 你是 Linux 系统吗?我这边下文件不是很方便,Linux 下百度网盘神马的太不友好了 T_T,你有没有试试把 A Bsorted 文件都放在 /tmp 下? 一般发行版默认 /tmp 挂载一半内存大小。我也想知道一下效率~
    coolloves
        20
    coolloves  
       2019-03-08 16:57:48 +08:00   ❤️ 1
    @necomancer 试试 baidupcs_go,可以跑满速,我刚下完,渣移动,100M 带宽,10MB 左右速度,差不多跑满了
    shuizhongyu10
        21
    shuizhongyu10  
    OP
       2019-03-08 17:31:14 +08:00
    @necomancer

    while read line
    do
    look ${line} Bsorted >> C
    done < A

    我用上面你提供的这个方法,测了这样输出 100 条所用的时间,不知道你要的是不是这个。
    放在硬盘和内存里都是 29s 左右。
    我系统是 ubuntu,/tmp 并没有挂载在内存上,我通过 mount -t tmpfs -o size=300m tmpfs dir 挂了一块内存来测的。
    按道理来说放在内存里应该有提升,不知道是测试的量太小还是哪里出了问题?
    hugee
        22
    hugee  
       2019-03-08 18:04:26 +08:00
    @shuizhongyu10 可以的。然后你说下你的执行时间大概是多少,如果我弄出来效率有提高就告知你。
    zclHIT
        23
    zclHIT  
       2019-03-08 18:42:26 +08:00 via iPhone
    zclHIT
        24
    zclHIT  
       2019-03-08 18:43:01 +08:00 via iPhone
    上面的链接应该就是你要做的吧?
    necomancer
        25
    necomancer  
       2019-03-08 19:16:55 +08:00
    @shuizhongyu10 我没处理过那么大量的 A,频繁打开文件确实是瓶颈,无论文件是否在内存里。刚才搜到一个帖子,看到使用 wile read line 本身就是一个很低效的办法,看来 shell 的确不适合直接做这种事...

    https://unix.stackexchange.com/questions/169716/why-is-using-a-shell-loop-to-process-text-considered-bad-practice

    ...😔
    zclHIT
        26
    zclHIT  
       2019-04-15 11:23:01 +08:00
    过去了一个月,问个新的问题,反过来要查找文件 a 中所有行,在文件 b 中没有的打印出来到文件 c 应该怎么写呢?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5512 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 07:10 · PVG 15:10 · LAX 23:10 · JFK 02:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.