技术解析

问个 shell 命令文件行提取问题
0
2021-06-21 04:16:40
idczone
两个文件 A,B,A 单列文件,B 多列文件
对 A 中每一行,到 B 中找对应第一列相同的行,提取出来到 C 文件中。
目前我是这样写的:
cat A | awk '{printf("grep %s B>>C\n",$1)}' | bash
但无奈实在有点慢,这里的 B 文件比较大,300w+行,A 文件 10w+行。
老哥们有什么方案能快点的吗?
你可能需要 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 加速。

或者使用 sgrep,我记得 sgrep 是需要排序好的文件的,用二分查找。

look 也是 binary search,但只能查行头。看实际需求吧。

因为你每次都要重新读一次 B 文件 - - 先存到内存里比较好,所以请换 PowerShell 或者脚本语言。
$b = {} 大小写不敏感,要得到敏感版本,使用 [System.Collections.Generic.Dictionary[string,string]]::new()
gc B | % { $b[$_.Substring(0, $_.IndexOf(' ') - 1)] = $_ }
gc A | % { $b[$_] } | sc C

建议把文本发出来,这样我们好测试代码执行速度。

每次读文件是个问题,sort -d B > /tmp/Bsorted,如果可以放内存里的话^_^

给个文本截图啊,如果分隔符都是空格应该一条 awk 命令搞定
awk 'NR==FNR{a[$1]=$0;next}NR>FNR{if($1 in a)print $0}' a b > c

感谢老哥 ,试了下你这种,速度差别不是很大 。最大瓶颈应该就是重复读入了 B 文件,我就是不知道怎么只读入一次

老哥。你这是个什么语言,我都不会用

牛逼了老哥,你这个速度贼快。你这种应该就是解决了多次读入 B 文件的问题,nb !

牛逼了老哥,你这个完美解决了多次读 B 的问题,A,B 文件都只读一次,速度贼快。

那我一会传网盘,放个链接?

awk 'BEGIN { while ( (getline line<"A") > 0 ) { hash[line] = 1 } } hash[$1] == 1 { print $0 }' B

A 和 B 都读取一次,先读 A 保存起来,再读 B 过滤,用 awk 处理很直接。
awk 'NR==FNR{a[$1];next}$1 in a' A B

这里应该改成 [email&然后 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 。
答案在 第一行

我只会 sort 之后,再 diff。awk 还是不够熟练。

不是 join 吗?

感谢感谢 PowerShell 有机会的话去学一下,目前采用的 awk 方法完全满足了需求。
测了下时间,只要 2.3s ,哈哈。

你是 Linux 系统吗?我这边下文件不是很方便,Linux 下百度网盘神马的太不友好了 T_T,你有没有试试把 A Bsorted 文件都放在 /tmp 下? 一般发行版默认 /tmp 挂载一半内存大小。我也想知道一下效率~

试试 baidupcs_go,可以跑满速,我刚下完,渣移动,100M 带宽,10MB 左右速度,差不多跑满了


while read line
do
look ${line} Bsorted >> C
done < A
我用上面你提供的这个方法,测了这样输出 100 条所用的时间,不知道你要的是不是这个。
放在硬盘和内存里都是 29s 左右。
我系统是 ubuntu,/tmp 并没有挂载在内存上,我通过 mount -t tmpfs -o size=300m tmpfs dir 挂了一块内存来测的。
按道理来说放在内存里应该有提升,不知道是测试的量太小还是哪里出了问题?

可以的。然后你说下你的执行时间大概是多少,如果我弄出来效率有提高就告知你。

https://m.linuxidc.com/Linux/2012-05/61174.htm

上面的链接应该就是你要做的吧?

我没处理过那么大量的 A,频繁打开文件确实是瓶颈,无论文件是否在内存里。刚才搜到一个帖子,看到使用 wile read line 本身就是一个很低效的办法,看来 shell 的确不适合直接做这种事...
https://unix.stackexchange.com/questions/169716/why-is-using-a-shell-loop-to-process-text-considered-bad-practice
...
数据地带为您的网站提供全球顶级IDC资源
在线咨询
专属客服