linux 文件查找_Linux中的文件内查找小技巧

写在前面的废话

这一周我为自己的马虎付出了严重的代价!!!每天十几个小时一直在debug……以后写代码之前一定要拜拜雍正,专治八阿哥(bug)

292e4d76185c8b8e8985e85d8e2be204.png

虽然debug让我无心其他事情,但是咱说好的周更不能断(其实是我找到了bug,想写篇文章压压惊)

太长不看系列

  • 最粗鲁的办法:grep -f (慢,内存需求太大)
  • 比较省事的办法:R的merge函数 (较快,但是内存需求贼大)
  • 比较机智的办法:字典/哈希(很快,Python里面叫作字典,Perl里面似乎叫作哈希,对于不会这两门语言的同学友好性太差)稳如老狗的办法:awk命令 (也是依赖字典方法,速度快,操作简单,深得吾心)

废话超多系列

我相信你们和我一样,有时需要知道文件A中有哪些行出现在文件B中。说起来有点绕,直接举个例子可能会更通俗易懂一些:

> cat A.txt
Hello
Thank you
Thank you very much
> cat B.txt
Hello
Welcome to
Beijing

文件A中有哪些行在文件B中也出现过,这个例子比较简单,我们直接就能看出A.txt中的Hello在B.txt文件中出现。

但是当文件较大时怎么办呢?一百行的话,你也许还可以试试 肉眼去观察,一旦行数上万,就必须借助计算机帮助我们完成这些事情了

不瞒你说,我之前查找的两个文件,一个几百万行,另一个上千万行,为了找一个合适的方法可是苦恼了很久

最粗鲁的办法:grep

grep方法比较粗鲁,但针对比较小的文件,我还是很喜欢用它的。因为它只需要敲六个字符,真的节省体力。cat -f A.txt B.txt  : 可以把B文件中存在于A文件中的行输出,具体操作如下47be2653ea8eecb08c5887fa9a8a2ee9.png

如果你使用了-v参数,则可以进行另一个骚操作,输出只存在与B文件而不存在与A文件中的行。但是这个方法有一些缺点,比如:

  • 不能分字段查找(比如A文件的第二列信息,是否在B文件中的第三列信息中出现)
  • 针对大文件,耗时长且内存消耗大(时间我没有专门统计过,但是在运行这个命令的时候内存倒是溢出过,如下图)51e08a51bbd839008593e36f2ca020e0.png

比较省事的办法:R的merge函数

做生信的同学,你可以不会Python,你可以不会Perl,但是R你总要会一点吧……

当我们在R中合并文件时,我们经常会用到merge()函数,它可以根据两文件中指定的列进行合并,对文件取交集。换个角度想想,这个取交集的操作不就是找把B文件中存在于A文件中的行么。

这里,因为篇幅问题,我就不介绍merge函数是如何使用的了。这个函数的原理我自己也不是很懂,只是知道是它用空间换时间,速度很快,但是内存消耗是真的大

下面是我对两个几百兆的文件merge时的报错信息,可以看到内存溢出的那是相当严重

af3254a13f77c30f076568825da72735.png

比较机智的办法:字典/哈希

如果你学过Python,一定知道dict()字典。这个方法是真的快,就和查字典一样,不存在遍历的问题。这里推荐阅读廖雪峰老师关于字典的介绍,我自己的语言功底很难三两句话把这个名词解释清楚。

  • https://www.liaoxuefeng.com/wiki/1016959663602400/1017104324028448

Python里面叫作字典,Perl里面似乎叫作哈希

但是该方法对于不会这两门语言的同学友好性太差,我只是想比对个文件,你却让我从头开始学一门编程语言???(内心:你这是想要我死????)7a39fd7b68b7f3b52fae64756701ebbf.png

稳如老狗的办法:awk命令

铺垫了这么多,终于可以扯到重点上了。awk/sed/grep可以说是shell中处理数据的三剑客,我们大部分的数据清洗问题,都可以使用这三个方法解决。

听起来这么牛,但是我们大部分人只会其基本操作,稍微复杂一点就会触碰到我们的知识盲区……

4c360336a1cf1004233f4a4ea7f18d6f.png

没关系,饭要一口一口吃,毕竟肥肉不是一天长成的,头发也不是一天就掉光的……

这里说个实话,希望不会挨打

这里简单介绍一下awk的一些参数,只介绍稍后用到的参数,想了解更多,可以自行搜索学习

  • FNR:各文件分别计数的行号。当awk命令后面跟了不止一个文件时,每读入一个新文件,行号就要从头开始计算。
  • NR:已经读出的记录数,就是行号,从1开始,不断累加,读入新文件也不会从头开始计算

    比如 awk命令之后跟了两个文件,分别有3行和5行,那么FNR的值依次是1,2,3,1,2,3,4,5;而NR的值则是1,2,3,4,5,6,7,8

  • 表示读取的文件某一行的所有内容,1表示某一行的第一列,$2表示某一行的第二列,以此类推
  • '[]':数组,比如a[$0]

介绍了这么多,我应该如何使用这些参数呢?

awk 'NR==FNR {a[$0]} NR>FNR&&!($0 in a){print $0}' A.txt B.txt

这里的NR==FNR,表示当前读取的是第一个文件A.txt的内容,这个时候创建一个数组,将第一个文件的每一行内容都作为一个key值输入,如果不存在这个a[$0]变量,则创建一个。通过这个方法,我们可以把文件A.txt的所有内容逐行放入数组变量a中。

接着NR>FNR表示,当前读取的不是第一个文件(即开始读取B.txt文件)。!($0 in a)表示当前读入的行,不存在之前的a数组中(即:B.txt文件中不存在于A.txt文件中的行)。中间的&&表示前后两个条件都要满足,若满足则执行print $0命令。

最终输出的结果将是只存在于B文件中,而不存在于A文件中的行。除此之外,我们可以指定比较两个文件中的特定列,比如:

awk 'NR==FNR {a[$3]} NR>FNR&&!($2 in a){print $0}' A.txt B.txt

以上命令表示,我想找出只存在于B文件中第二列,而不存在于A文件中第三列的字段,若存在这样的字段,则将B文件中该字段所在的行打印输出。

这个方法,速度快,容易上手,基本上你会用Linux就可以。既然提到了awk命令,那就再说一个骚操作:文件去重!!!

好吧,我知道一旦说去重,你首先想到的一定是以下两条命令:

  1. sort A.txt | uniq
  2. sort -u A.txt

这个方法固然是好,但是sort命令排序十分耗时。处理小文件时我们可能体会不到,但是一旦遇到上百万行的文件,光是等待时间就够我们喝一壶茶了。这个时候awk命令就体现出其重要性了。命令如下:

awk '!a[$0]++' A.txt

这里不讲太多,简单说一下这个命令执行的步骤:

  1. 首先执行a[$0],将输入文件的一整行当作数组a的key值
  2. 接着执行!a[$0],对a[$0]的值取反,如果a[$0]返回值为0,则整个表达式!a[$0]为1,执行awk的默认操作,打印输出这一行
  3. 最后是!a[$0]++,对a[$0]的值加1,因此当之后再遇到相同行时,!a[$0]会被看作是0,不hi行默认的打印操作

也许你觉得我讲的还是不够清楚,那么这里给你推荐一个问答,里面的第一个回答(最高赞)将这个去重的逻辑讲的十分清楚。

  • https://unix.stackexchange.com/questions/159695/how-does-awk-a0-work
一点题外话

大部分做生信的人,对于算法都是一知半解,只求解决问题不求代码优美。但写代码,我们毕竟不是专业的。我们的终极目的是解决生物学问题,而不是过多的纠结代码的优美性。

代码写的再好看,解决不了问题,依然发不了sci

另外代码不要晚上写,一个原因是晚上写代码容易出bug(这是一个涉及玄学的话题),另外一个原因就是熬夜令人头秃。

最近头有点凉,也不知道是天气冷了,还是头发秃了……

作者的生发剂就靠你们的打赏和广告点击量了

8c68e8fb56f0de8f23a9f57c93d405b6.png

49642083a32bc68be954d024e7e06836.png

d6911635b661df46ed28300b497d8e28.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值