【shell笔记>文本处理|专项】Linux数据文本处理工
接1
file
查看文件编码
wsx@wsx-ubuntu:~$ file regular_express.txt
regular_express.txt: ASCII text, with CRLF, LF line terminators
常用的大型数据文件一般存为ASCII码形式(像几大基因bank的数据文件),而我们自己认为创建的常为UTF-8,所以有时候认为处理文件需要会碰到把UTF-8编码的字符插入到ASCII码文件里去了。遇到这种问题,我们可以用hexdump -c
命令查看出错的地方(手边没有这样的文件,就不举例了)。
用sort
对文本排序
我们先创建一个bed格式文件来试试这个命令:
wsx@wsx-ubuntu:~$ cat test.bed
chr1 26 39
chr3 32 47
chr1 40 50
chr1 9 28
chr2 35 54
chr1 10 19
wsx@wsx-ubuntu:~$ sort test.bed
chr1 10 19
chr1 26 39
chr1 40 50
chr1 9 28
chr2 35 54
chr3 32 47
可以明显看到文本按照第一列进行了排序。
默认,sort
用空格或tab键作为域(列)分隔符。如果我们用其他形式的分隔符,需要用-t
选项指定。
下面是对bed
文件最通用的排序命令:
wsx@wsx-ubuntu:~$ sort -k1,1 -k2,2n test.bed
chr1 9 28
chr1 10 19
chr1 26 39
chr1 40 50
chr2 35 54
chr3 32 47
基本操作bedtools
软件都会先用这个命令对bedtools
文件排序。
现在略加解释一下,sort
用-k
选项指定某列的排序方式。而每次使用-k
选项都要带上指定列的范围(start, end)。如果只指定一列,就为(start,start)了,像上面命令的-k1,1
就是。也许你会觉得-k2,2n
很奇怪,这里的n
指定程序把第二列当做数值对待。如果不做设定,都是当做字符对待(shell都是这么对待数值数据的)。所以总结其他这一行命令就是对第一列按照字符排序,第二列按照数值排序。
我们可以用-c
选项检查一个文件是不是已经按照过某种方式排过序了。
wsx@wsx-ubuntu:~$ sort -k1,1 -k2,2n test.bed | sort -k1,1 -k2,2 -c
sort:-:2:无序: chr1 10 19
wsx@wsx-ubuntu:~$ echo $?
1
wsx@wsx-ubuntu:~$ sort -k1,1 -k2,2n test.bed | sort -k1,1 -k2,2n -c
wsx@wsx-ubuntu:~$ echo $?
0
上面可以清楚地看到sort
是怎么对待文件的(一般shell返回0表示成功执行)。
wsx@wsx-ubuntu:~$ tsfds
tsfds:未找到命令
wsx@wsx-ubuntu:~$ echo $?
127
wsx@wsx-ubuntu:~$ echo test
test
wsx@wsx-ubuntu:~$ echo $?
0
shell的命令退出状态码表示了该命令执行的完成的某种情况。不同的状态码有不同的含义,具体可以百度查阅(我之前整理的shell笔记应该讲过,可以看看)。
反向排序用-r
选项。如果你只想反转一列,可以把它加在-k
选项后。
wsx@wsx-ubuntu:~$ sort -k1,1 -k2,2nr test.bed
chr1 40 50
chr1 26 39
chr1 10 19
chr1 9 28
chr2 35 54
chr3 32 47
现在我给test.bed
加一行:
wsx@wsx-ubuntu:~$ cat test.bed
chr1 26 39
chr3 32 47
chr1 40 50
chr1 9 28
chr2 35 54
chr1 10 19
chr11 22 56
你会发现有点奇怪
wsx@wsx-ubuntu:~$ sort -k1,1 -k2,2n test.bed
chr1 9 28
chr1 10 19
chr1 26 39
chr1 40 50
chr11 22 56
chr2 35 54
chr3 32 47
怎么chr11
在chr2
前面?其实sort
排序的方式有点像查字典。例子中,命令先比较c
,然后比较h
,然后比较r
,接着比较1
,自然11
会在2
前面了。这里可以添加V
选项修改。
wsx@wsx-ubuntu:~$ sort -k1,1V -k2,2n test.bed
chr1 9 28
chr1 10 19
chr1 26 39
chr1 40 50
chr2 35 54
chr3 32 47
chr11 22 56
是不是觉得这样更可观一些?不过通常在处理数据时不做此处理,符合 规范的数据可以让后续处理程序效率更高。
基本掌握sort
这些也够用了,它主要为后续处理服务。如果想知道其他的用法,查查吧,同时欢迎发文来交流。
用uniq
寻找唯一值
首先创建样例文本
wsx@wsx-ubuntu:~$ cat test.letter
A
A
B
C
B
C
C
C
D
F
D
使用uniq
看看
wsx@wsx-ubuntu:~$ uniq test.letter
A
B
C
B
C
D
F
D
尼玛,怎么不对。它好像只去掉了连续的同一字符。怎么办?想想我们刚学了什么命令?sort
不是刚好可以把同样的字符弄到一起去吗,然后再使用uniq
,嘿嘿:
wsx@wsx-ubuntu:~$ sort test.letter | uniq
A
B
C
D
F
哟呵,got you。
加-c
选项计数:
wsx@wsx-ubuntu:~$ sort test.letter | uniq -c
2 A
2 B
4 C
2 D
1 F
把结果再排序
wsx@wsx-ubuntu:~$ sort test.letter | uniq -c | sort -rn
4 C
2 D
2 B
2 A
1 F
-d
选项只输出重复行
wsx@wsx-ubuntu:~$ cat test.letter
A
A
B
C
B
C
C
C
D
F
D
wsx@wsx-ubuntu:~$ uniq -d test.letter
A
C
wsx@wsx-ubuntu:~$ sort test.letter | uniq -d
A
B
C
D
使用时需要注意处理不同导致的结果差异。
Join 命令
用来连接文件。
假设现在我们有两个文件:
wsx@wsx-ubuntu:/tmp$ cat example.bed
chr1 26 39
chr1 32 47
chr3 11 28
chr1 40 49
chr3 16 27
chr1 9 28
chr2 35 53
wsx@wsx-ubuntu:/tmp$ cat example_length.txt
chr1 53453
chr2 34356
chr3 24356
我想把第二个文件说明染色体长度添加到第一个文件对应染色体的第三列。
我们首先要给文件排序(使用join
前必须做),然后使用join
命令。
wsx@wsx-ubuntu:/tmp$ sort -k1,1 example.bed > example_sorted.bed
wsx@wsx-ubuntu:/tmp$ sort -c -k1,1 example_length.txt
wsx@wsx-ubuntu:/tmp$ cat example_sorted.bed
chr1 26 39
chr1 32 47
chr1 40 49
chr1 9 28
chr2 35 53
chr3 11 28
chr3 16 27
wsx@wsx-ubuntu:/tmp$ join -1 1 -2 1 example_sorted.bed example_length.txt > example_with_length.txt
wsx@wsx-ubuntu:/tmp$ cat example_with_length.txt
chr1 26 39 53453
chr1 32 47 53453
chr1 40 49 53453
chr1 9 28 53453
chr2 35 53 34356
chr3 11 28 24356
chr3 16 27 24356
命令基本语法是
join -1 <file_1_field> -2 <file_2_field> <file_1> <file_2>
既然名字叫join
,就是两者必须有共同之处,通过共同的支点将两者连为一体。
-1
和-2
选项后接参数分别指定了这个支点,也就是连接的域(列)。比如例子中,都是两个文件的第一列。
两个文件中,第一列都共有chr1(2)(3)
。 如果不一致会出现什么情况呢?
wsx@wsx-ubuntu:/tmp$ join -1 1 -2 1 example_sorted.bed example_length_alt.txt chr1 26 39 53453
chr1 32 47 53453
chr1 40 49 53453
chr1 9 28 53453
chr2 35 53 34356
如果第二个文件没有chr3
,join
之后也没了!!
我们可以通过-a
选项指定哪一个文件可以不遵循配对
wsx@wsx-ubuntu:/tmp$ join -1 1 -2 1 -a 1 example_sorted.bed example_length_alt.txt
chr1 26 39 53453
chr1 32 47 53453
chr1 40 49 53453
chr1 9 28 53453
chr2 35 53 34356
chr3 11 28
chr3 16 27
awk
awk
是文本处理的一把好手,虽然它不能像python
,R
干一些高级复杂的主题工作,但是它具备完整的命令操作和编程体系。
awk
是一门语言,我不可能在学习的时候能够逻辑清晰详细地介绍给大家。主要是还通过实例来了解用法,加深认识。手册可以参考http://man.linuxde.net/awk。
首先要明白的是,awk
按行处理数据。在shell知识里,如果把一个文档看做一张表。那么一行就是一个记录,一列就是一个域。可以看出,awk
就是按记录处理文本的。
其次是awk
的程序结构是
pattern {action}
pattern可以是表达式或者正则表达式。pattern有点像if
语句,当它满足时就会执行相应的动作。
另一个awk
核心是它用$0
表示所有列,$1
,$2
...等等表示对应的列。我们可以很方便地用它进行操作。
wsx@wsx-ubuntu:/tmp$ awk '{print $0}' example.bed
chr1 26 39
chr1 32 47
chr3 11 28
chr1 40 49
chr3 16 27
chr1 9 28
chr2 35 53
wsx@wsx-ubuntu:/tmp$ awk '{print $1}' example.bed
chr1
chr1
chr3
chr1
chr3
chr1
chr2
wsx@wsx-ubuntu:/tmp$ awk '{print $2}' example.bed
26
32
11
40
16
9
print
语句就像动作一样输出你操作的结果。
wsx@wsx-ubuntu:/tmp$ awk '{ print $2 "\t" $3}' example.bed
26 39
32 47
11 28
40 49
16 27
9 28
35 53
wsx@wsx-ubuntu:/tmp$ awk '{ print $2 $3}' example.bed
2639
3247
1128
4049
1627
928
3553
wsx@wsx-ubuntu:/tmp$ awk '{ print $2 , $3}' example.bed
26 39
32 47
11 28
40 49
16 27
9 28
35 53
了解上述几个语句的不同。
表示染色体名一般用带chr
或者不带chr
标志两种方式。当我们要用到这两种时,肯定要让它们能够对应起来,也就是转换。awk
命令可以非常方便地添加chr
标记。
下面我先把例子文件的chr
去掉,然后加上试试。
wsx@wsx-ubuntu:/tmp$ awk '{ print $1}' example.bed
chr1
chr1
chr3
chr1
chr3
chr1
chr2
wsx@wsx-ubuntu:/tmp$ awk '{ print $1}' example.bed | cut -c4
1
1
3
1
3
1
2
wsx@wsx-ubuntu:/tmp$ awk '{ print $1}' example.bed | cut -c4 | awk '{print "chr"$1}'
chr1
chr1
chr3
chr1
chr3
chr1
chr2
awk
作为一门编程语言,它支持各种操作符(运算,逻辑,判断)喔。
wsx@wsx-ubuntu:/tmp$ awk '$3 - $2 >18' example.bed
chr1 9 28
wsx@wsx-ubuntu:/tmp$ awk '$1 ~/chr1/ && $3 - $2 > 10' example.bed
chr1 26 39
chr1 32 47
chr1 9 28
# 这里 ~ 符号用来匹配正则表达式
还有awk
存在一些变量,像NR
表示行号,OFS
表示输出分隔符等。
wsx@wsx-ubuntu:/tmp$ awk 'NR >= 3 && NR <= 5' example.bed
chr3 11 28
chr1 40 49
chr3 16 27
如果我们想把gtf
文件转换成为bed
格式,可以使用
wsx@wsx-ubuntu:~/Work/research/Promoter_Research$ head -n1000 Homo_sapiens.GRCh37.75.gtf | awk '!/^#/{ print $1 "\t" $4-1 "\t" $5} ' | head -n 3
1 11868 14412
1 11868 14409
1 11868 12227
因为篇幅有限,我不可能输出所有结果,所以只取部分数据做了运算。