大纲
1、join是什么?有什么作用?
2、join语法格式
3、实战演练
4、引入paste命令
1、join是什么?有什么作用?
Linux下最常用的数据文件格式是文本格式的,使用分隔符来区分不同的字段,比如冒号(:)、制表符、空格等。像常见的/etc/passwd和
/etc/group两个文件就是用":"来分隔的。这种文本格式我们可以视为文本数据库,既方便人去阅读,也适合程序处理,通常某列类似于数据库中的关键字。
join命令就是一个根据关键字合并数据文件的命令(join lines of two files on a common
field),类似于关系型数据库中两张表关联查询。
有过关系型数据库使用经验的同学都应该知道,当我们组合多个表进行查询时,通常要指定表之间如何关联的条件,否则产生笛卡尔积。下例展示了如何通过 CategoryID字段联接
Categories和 Products表:
SELECT CategoryName, ProductName FROM Categories INNER JOIN Products ON Categories.CategoryID = Products.CategoryID;
如果了解关系型数据库的关联查询,那么join命令的工作原理很容易理解。
2、join语法格式
语法我们可以通过 join --help 命令查看:
Usage: join [OPTION]... FILE1 FILE2 For each pair of input lines with identical join fields, write a line to standard output. The default join field is the first, delimited by whitespace. When FILE1 or FILE2 (not both) is -, read standard input. -a FILENUM also print unpairable lines from file FILENUM, where FILENUM is 1 or 2, corresponding to FILE1 or FILE2 -e EMPTY replace missing input fields with EMPTY -i, --ignore-case ignore differences in case when comparing fields -j FIELD equivalent to ‘-1 FIELD -2 FIELD‘ -o FORMAT obey FORMAT while constructing output line -t CHAR use CHAR as input and output field separator -v FILENUM like -a FILENUM, but suppress joined output lines -1 FIELD join on this FIELD of file 1 -2 FIELD join on this FIELD of file 2
帮助文档这样说到:
“For each pair of input lines with identical join fields, write a line to standard output. The default join field is the first, delimited by whitespace. When FILE1 or FILE2 (not both) is -, read standard input.”
join根据公共字段(关键字)来合并两个文件的数据行,然后送到标准输出。默认以空白(whitespace)分隔数据,并且默认比对第一个字段。
常用的有以下几个选项:
-t CHAR
指定分隔符,比如 -t‘:‘则使用:作为分隔符,默认分隔符为空白[空格+TAB+换行](whitespace)。
-1 FIELD
指定第1个文件使用哪一个字段进行Join
-2 FIELD
指定第2个文件使用哪一个字段进行join
-a FILENUM
相当于OUTER JOIN
-o <FILENO.FIELDNO> ...
其中FILENO=1表示第1个文件,FILENO=2表示第2个文件,FIELDNO表示字段序号,从1开始编号。默认会全部输出,但关键字列只输出一次。
比如:-o 1.1 1.2 2.2 表示输出第一个文件的第一个字段、第二个字段,第二个文件的第二个字段。
join与SQL关联类比:
内连接(inner join) 格式:join <FILE1> <FILE2> 左连接(left join, 左外连接, left outer join) 格式:join -a1 <FILE1> <FILE2> 右连接(right join, 右外连接,right outer join) 格式:join -a2 <FILE1> <FILE2> 全连接(full join, 全外连接, full outer join) 格式:join -a1 -a2 <FILE1> <FILE2>
3、实战演练
Important: FILE1 and FILE2 must be sorted on the join fields.
需要注意的是:在使用join之前,文件最好使用sort进行排序处理。
准备测试数据:
# head month_en.txt month_zh.txt ==> month_en.txt <== 1 January 2 February 3 March 14 "Unknown" ==> month_zh.txt <== 1 一月 2 二月 3 三月 13 "十三月,故意的"
a、inner join
join 没有指定任何参数的情况下,默认使用whitespace作为分隔符,并使用第一个字段作为join关键字,忽略关键字不匹配的行。
# join month_en.txt month_zh.txt 1 January 一月 2 February 二月 3 March 三月
b、左外连接(left outer join)
join -a1 month_en.txt month_zh.txt 1 January 一月 2 February 二月 3 March 三月 14 "Unknown"
这次把"14 Unknown"这一行也显示出来了,也就是说把month_en.txt中不匹配的行也显示出来。
c、右外连接(right outer join)
# join -a2 month_en.txt month_zh.txt 1 January 一月 2 February 二月 3 March 三月 13 "十三月,故意的"
这次显示”十三月,故意的”这一行显示出来,也就是把month_zh.txt不匹配的行显示出来。
d、全连接(full join)
# join -a1 -a2 month_en.txt month_zh.txt 1 January 一月 2 February 二月 3 March 三月 13 "十三月,故意的" 14 "Unknown"
看到了吧,效果是很明显的。
e、指定分隔符
我们把/etc/passwd和/etc/shadow整合起来。
# head -n3 /etc/passwd /etc/shadow [sudo] password for yy: ==> /etc/passwd <== root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin ==> /etc/shadow <== root:!:16212:0:99999:7::: daemon:*:16177:0:99999:7::: bin:*:16177:0:99999:7:::
我们看到,这两个文件都是以":"作为分隔符,我们以第一个字段作为join关键字
# join -t: /etc/passwd /etc/shadow root:x:0:0:root:/root:/bin/bash:!:16212:0:99999:7::: daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin:*:16177:0:99999:7::: bin:x:2:2:bin:/bin:/usr/sbin/nologin:*:16177:0:99999:7:::
OK, 看到了吧,是不是非常方便。这里主要是用到SQL联合查询的概念。
4、引入paste命令
paste有什么作用呢?paste仅仅简单的把两个文件的行粘在一起,构成新行的字段默认以TAB分隔。它相比与join命令就简单得多了,join命令必须根据两个文件相关联的关键字,然后再进行合并,而paste什么都不考虑,直接进行合并。
# paste month_en.txt month_zh.txt 1 January 1 一月 2 February 2 二月 3 March 3 三月 14 "Unknown" 13 "十三月,故意的"
看出paste和join的差别了吗?当然,可以使用-d选项指定构成新行的分隔符。
Linux文本处理工具之join,paste,布布扣,bubuko.com