继续总结一下linux 的文本处理。包括但不限于awk, sed, paste,split,grep....
接上文[[18-详说linux文本处理(一)]]
paste 可以实现类似R 中paste 的功能,不过其是对文件进行操作:
$ paste -d ':' test3 test4
1:10
2:9
3:8
4:7
5:6
6:5
7:4
8:3
9:2
10:1
其主要选项为:
-d # 指定文件合并的分隔符,默认为tab
-s # 将文件合并后再转置
如果存在不对齐的情况,则paste 会保留空位:
sed -i '2d' test3
$ paste -s test3 test4
1 3 4 5 6 7 8 9 10
10 9 8 7 6 5 4 3 2 1
paste 还有一个比较好用特性,其可以将一个文件拆分为若干列:
$ cat test3 | paste - - -
1 3 4
5 6 7
8 9 10
我经常喜欢对比的一个例子是fq 转fa 的操作:
# awk 高级玩家
awk '{if(NR%4 == 1){print ">" substr($0, 2)}}{if(NR%4 == 2){print}}' fastq > fasta
# paste 偷懒玩家
cat fastq | paste - - - - | awk '{print $1"\n"$4}' | tr '@' '>' > fasta
# sed 勤劳玩家
less -S fastq | awk '{print$1}' | sed -n -e '1~4p' -e '2~4p' | sed 's/@/>/' > fasta
上面的awk 是不是看起来很复杂,复杂就对了。这玩意儿贼难用。比sed 还难!
awk 和sed 的结构一样,也是三段式:
awk -options script files
awk 的options 中,-F
用来设置字段分隔符。
这里使用数据:
$ head mtcars2.csv
mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb
21,6,160,110,3.9,2.62,16.46,0,1,4,4
21,6,160,110,3.9,2.875,17.02,0,1,4,4
22.8,4,108,93,3.85,2.32,18.61,1,1,4,1
21.4,6,258,110,3.08,3.215,19.44,1,0,3,1
18.7,8,360,175,3.15,3.44,17.02,0,0,3,2
18.1,6,225,105,2.76,3.46,20.22,1,0,3,1
14.3,8,360,245,3.21,3.57,15.84,0,0,3,4
24.4,4,146.7,62,3.69,3.19,20,1,0,4,2
22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2
awk 在读取文本时,会将预定义的字段分隔符
划分给每个数据字段,并分配一个变量。awk 默认的字段分隔符为任意空白字符(空格或制表符),可以用 -F 参数定义字段分隔符。字段变量对应关系如下:
$0 代表整个文本行
$1 代表文本中第一个数据字段
...
$NF 代表文本行中的最后一个数据字段
此外,还有如下变量:
FS # 输入字段分隔符,类似-F 参数设定分隔符;
RS # 输入记录分隔符
OFS # 输出字段分隔符
ORS # 输出记录分隔符
NF # 字段总数,比如列数
NR # 输入记录数,比如行数
结合script 中的print 我们可以打印数据的列数,结合wc 了解数据行列:
$ wc -l mtcars2.csv; head -1 mtcars2.csv | awk -F ',' '{print NF}'
33 mtcars2.csv
11
awk 的匹配结构,和sed 类似,通过/xxx/
来匹配符合字段的行,并返回输出,接着通过print 打印:
$ cat mtcars2.csv | awk -F ',' '/drat/ {print}'
mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb
我们可以使用BEGIN 与END 语句来对awk 处理的相关内容进行先后设定,相当于 BEGIN >> awk >> END 对应语句依次进行。
$ cat mtcars2.csv | awk -F ',' 'BEGIN{print "---start---"} /18/ {print NR} END{print "---finish---"}'
---start---
4
6
7
11
12
13
14
15
20
23
27
33
---finish---
还可以通过修改内置变量OFS,改变输出分割格式:
$ cat mtcars2.csv | awk 'BEGIN{FS = ",";OFS="\t"} /18/ {print $1,$NF} END{print "---finish---"}'
22.8 1
18.7 2
18.1 1
19.2 4
17.8 4
16.4 3
17.3 3
15.2 3
30.4 2
15.5 2
27.3 1
21.4 2
---finish---
我们可以将awk 的{}
中的内容,当做一个单独的编程语言。
形如:awk '{if (condition) {yes} else {no}}'
$ cat mtcars2.csv | awk 'BEGIN{FS = ",";OFS="\t"} {if ($2 == 8) {print $1,$NF} else {print "it is not equal to 8"}} END{print "---finish---"}' | head
it is not equal to 8
it is not equal to 8
it is not equal to 8
it is not equal to 8
it is not equal to 8
18.7 2
it is not equal to 8
14.3 4
it is not equal to 8
it is not equal to 8
awk '{for (condition) {statement} }'
$ cat mtcars2.csv | awk 'BEGIN{FS = ",";OFS="\t"} {if ($2 == 8) {print $2-$1}} END{print "---finish---"}' | head
-10.7
-6.3
-8.4
-9.3
-7.2
-2.4
-2.4
-6.7
-7.5
-7.2
直接对列计算处理即可,比如:
+, -, *, ^, /
比如希望过滤文件的第一行,可以使用:
$ awk 'NR == 1 {next} {print $0}' file
当然这个操作sed 也可以实现。
-F 参数可以指定正则,设定多个分隔符,比如:[\t,]
将字符进行替换压缩和删除。
echo -e 'good \t good' | tr '\t' ';'
good ; good
echo "HELLO WORLD" | tr 'A-Z' 'a-z'
hello world
# ‘A-Z’ 和 ‘a-z’都是集合,集合是可以自己制定的,例如:’ABD-}’、‘bB.,’、‘a-de-h’、‘a-c0-9’都属于集合,集合里可以使用’‘、’,可以可以使用其他ASCII字符。
echo "hello 123 world 456" | tr -d '0-9'
hello world
和数据库或者其他编程语言中的join 非常类似,就是将文件通过相同的行连接在一起。
$ cd /home/shiyanlou
# 创建两个文件
$ echo '1 hello' > file1
$ echo '1 shiyanlou' > file2
$ join file1 file2
# 将/etc/passwd与/etc/shadow两个文件合并,指定以':'作为分隔符
$ sudo join -t':' /etc/passwd /etc/shadow
# 将/etc/passwd与/etc/group两个文件合并,指定以':'作为分隔符, 分别比对第4和第3个字段
$ sudo join -t':' -1 4 /etc/passwd -2 3 /etc/group
# 也就是以: 分隔passwd 与group 中的内容,其中取前者的第四段和后者的第三段合并
搜索引擎是个好东西,不会查一下就好了:
如果你想了解awk 与sed,有一本书:
要学习更多的命令,可以参考:Linux 命令大全 | 菜鸟教程[1]
utools 也有个好用的插件:
[1]
Linux 命令大全 | 菜鸟教程: https://www.runoob.com/linux/linux-command-manual.html