Contents

Linux中join命令简介

1. 概述

在本教程中,我们将探讨如何使用join 命令并举例说明。

2. join

join命令使我们能够使用每个文件中的公共字段作为文件中相关行之间的链接将两个文件合并在一起

当我们想要连接关系数据库中的两个或多个表时,我们可以像考虑 SQL 连接一样考虑 Linux join命令。

为此,我们需要在每个表中都有一个公共字段,我们可以使用它来形成它们之间的关系。当我们在 Linux 中加入文件时也是如此——我们需要有一种方法让join知道我们文件中的行是如何相互关联的。

如果我们不注意对输入文件进行排序 ,我们可能会得到不准确的结果。我们必须按公共字段对输入文件进行排序

3. 例子

让我们首先创建一些示例文件以在我们的示例中使用。我们将创建一个场景,在该场景中,我们希望使用设备的 IP 地址作为公共字段来组合两个日志文件。

我们的第一个文件将包含设备的 IP 地址和机器名称:

$ cat << EOF > device_names.log
10.0.1.10 WINSHARE01
10.0.1.13 WEBSERVER03
10.0.1.15 FINSERVER02
143.204.74.129 WEBSERVER02
152.120.106.21 HRSERVER01
192.168.8.28 MYWORKSTATION
192.168.10.4 PRINTER02
EOF

对于我们的第二个文件,我们将添加一个定义每个设备的 Internet 访问级别的 Internet 浏览策略:

$ cat << EOF > device_policies.log
10.0.1.10 RED
10.0.1.13 YELLOW
10.0.1.15 RED
143.204.74.129 YELLOW
152.120.106.21 BLUE
192.168.8.28 GREEN
192.168.10.4 RED
EOF

在进入一些示例之前,让我们讨论一下我们的示例数据。每个文件都以 IP 地址作为第一个字段开始,我们这样做是因为默认情况下,** join期望第一个字段是文件之间的公共字段**。此外,我们使用空格字符作为字段之间的分隔符,这也是join的默认值

3.1. 加入排序文件

由于我们已经注意按 IP 地址对两个日志文件进行排序并使用默认的空格分隔符,因此我们能够运行最简单的join命令调用:

$ join device_names.log device_policies.log
10.0.1.10 WINSHARE01 RED
10.0.1.13 WEBSERVER03 YELLOW
10.0.1.15 FINSERVER02 RED
143.204.74.129 WEBSERVER02 YELLOW
152.120.106.21 HRSERVER01 BLUE
192.168.8.28 MYWORKSTATION GREEN
192.168.10.4 PRINTER02 RED

我们生成的输出显示每行每个相关设备的 IP 地址、设备名称和 Internet 策略。

3.2. 加入未排序的文件

让我们创建一个device_policies.log的副本,稍微改变一下顺序,看看我们如何处理未排序的文件:

$ cat << EOF > device_policies_unordered.log
10.0.1.10 RED
10.0.1.13 YELLOW
10.0.1.15 RED
143.204.74.129 YELLOW
192.168.8.28 GREEN
192.168.10.4 RED
152.120.106.21 BLUE
EOF

现在让我们使用我们的device_policies_unordered.log文件来发出基本的join命令:

$ join device_names.log device_policies_unordered.log
10.0.1.10 WINSHARE01 RED
10.0.1.13 WEBSERVER03 YELLOW
10.0.1.15 FINSERVER02 RED
143.204.74.129 WEBSERVER02 YELLOW
join: device_names.log:7: is not sorted: 192.168.10.4 PRINTER02
join: device_policies_unordered.log:6: is not sorted: 192.168.10.4 RED
192.168.8.28 MYWORKSTATION GREEN
192.168.10.4 PRINTER02 RED

我们的输出现在看起来不同了。我们也看到了一些信息性消息。我们的文件排序不一样,这就是我们在输出中看到错误消息的原因join继续匹配文件中正确对应的行。

幸运的是,我们有一种方法可以在执行合并之前测试我们的输入文件。我们传递了–check-order标志,以便join 检查我们的输入文件是否正确排序。*让我们看看如果我们再次尝试加入不匹配的文件会发生什么,这次使用–check-order*标志:

$ join --check-order device_names.log device_policies_unordered.log
10.0.1.10 WINSHARE01 RED
10.0.1.13 WEBSERVER03 YELLOW
10.0.1.15 FINSERVER02 RED
143.204.74.129 WEBSERVER02 YELLOW
join: device_names.log:7: is not sorted: 192.168.10.4 PRINTER02

在此示例中,join开始合并记录,但在发现第一个未正确排序的记录实例时停止。 例如,当在脚本中使用连接作为步骤时,有时我们可能希望忽略不匹配的记录并继续连接正确排序的记录。–nocheck -order标志为我们提供了确切的行为。

让我们再次尝试加入我们不匹配的文件,而不用担心不匹配的行:

$ join --nocheck-order device_names.log device_policies_unordered.log
10.0.1.10 WINSHARE01 RED
10.0.1.13 WEBSERVER03 YELLOW
10.0.1.15 FINSERVER02 RED
143.204.74.129 WEBSERVER02 YELLOW
192.168.8.28 MYWORKSTATION GREEN
192.168.10.4 PRINTER02 RED

我们的输出显示,通过在 join 中使用–nocheck-order*标志,我们已经成功处理了文件并接收了所有匹配的行,同时忽略了那些没有意外失败或错误消息的行。*

3.3. 使用自定义分隔符连接文件

我们需要一种方法来为join提供自定义分隔符。通过使用-t*选项将值传递给 join ,我们为 join 提供了要使用的自定义分隔符。*

让我们更新我们的日志文件并更改使用逗号作为字段之间的分隔符:

$ cat << EOF > device_names_comma.log
10.0.1.10,WINSHARE01
10.0.1.13,WEBSERVER03
10.0.1.15,FINSERVER02
143.204.74.129,WEBSERVER02
152.120.106.21,HRSERVER01
192.168.8.28,MYWORKSTATION
192.168.10.4,PRINTER02
EOF
$ cat << EOF > device_policies_comma.log
10.0.1.10,RED
10.0.1.13,YELLOW
10.0.1.15,RED
143.204.74.129,YELLOW
152.120.106.21,BLUE
192.168.8.28,GREEN
192.168.10.4,RED
EOF

现在让我们尝试合并这两个以逗号分隔的新日志文件:

$ join -t , device_names_comma.log device_policies_comma.log
10.0.1.10,WINSHARE01,RED
10.0.1.13,WEBSERVER03,YELLOW
10.0.1.15,FINSERVER02,RED
143.204.74.129,WEBSERVER02,YELLOW
152.120.106.21,HRSERVER01,BLUE
192.168.8.28,MYWORKSTATION,GREEN
192.168.10.4,PRINTER02,RED

我们已经使用自定义分隔符成功合并了我们的记录。

3.4. 使用第一个文件中的不同字段连接文件

我们的公共字段可能并不总是每个文件中的第一个字段。我们需要能够使用以不同顺序出现的字段来连接文件。让我们更改device_names.log文件中字段的顺序:

$ cat << EOF > device_names_reverse.log
WINSHARE01 10.0.1.10
WEBSERVER03 10.0.1.13
FINSERVER02 10.0.1.15
WEBSERVER02 143.204.74.129
HRSERVER01 152.120.106.21
MYWORKSTATION 192.168.8.28
PRINTER02 192.168.10.4
EOF

如果我们尝试加入device_names_reverse.logdevice_policies.log,我们可以预期操作会失败,因为公共字段——IP 地址——不再是两个文件中的第一个字段。

幸运的是,我们可以覆盖join的默认行为并传入第一个文件中的字段编号以用作公共字段:

$ join -1 2 device_names_reverse.log device_policies.log
10.0.1.10 WINSHARE01 RED
10.0.1.13 WEBSERVER03 YELLOW
10.0.1.15 FINSERVER02 RED
143.204.74.129 WEBSERVER02 YELLOW
152.120.106.21 HRSERVER01 BLUE
192.168.8.28 MYWORKSTATION GREEN
192.168.10.4 PRINTER02 RED

通过使用*-1选项,我们可以连接两个文件中公共字段顺序不同的文件。我们只需将第一个文件中公共字段的位置作为-1选项的值传递。-1选项非常简单地引用join*命令的参数中列出的第一个文件。

3.5. 使用第二个文件中的不同字段连接文件

现在让我们稍微改变一下参数的顺序,看看我们如何为第二个文件实现相同的行为:

$ join -2 2 device_policies.log device_names_reverse.log
10.0.1.10 RED WINSHARE01
10.0.1.13 YELLOW WEBSERVER03
10.0.1.15 RED FINSERVER02
143.204.74.129 YELLOW WEBSERVER02
152.120.106.21 BLUE HRSERVER01
192.168.8.28 GREEN MYWORKSTATION
192.168.10.4 RED PRINTER02

我们已经从使用*-1选项更改为使用-2*,它引用参数列表中的第二个文件。我们将公共字段的位置作为*-2*选项的值传递。

让我们进行另一个快速更改并颠倒device_policies.log文件中字段的顺序:

$ cat << EOF > device_policies_reverse.log
RED 10.0.1.10 
YELLOW 10.0.1.13 
RED 10.0.1.15 
YELLOW 143.204.74.129 
BLUE 152.120.106.21 
GREEN 192.168.8.28 
RED 192.168.10.4 
EOF

3.6. 使用两个文件中的不同字段连接文件

我们已经切换了文件的顺序,将策略组作为第一个字段,将 IP 地址作为第二个字段。这与我们的device_names_reverse.log文件的顺序相同。所以现在我们的公共字段——IP 地址——在两个文件中的位置相同。

让我们看看我们如何调用 join 来满足这种情况:

$ join -j 2 device_names_reverse.log device_policies_reverse.log
10.0.1.10 WINSHARE01 RED
10.0.1.13 WEBSERVER03 YELLOW
10.0.1.15 FINSERVER02 RED
143.204.74.129 WEBSERVER02 YELLOW
152.120.106.21 HRSERVER01 BLUE
192.168.8.28 MYWORKSTATION GREEN
192.168.10.4 PRINTER02 RED

我们使用*-j选项来指示join*为每个相应文件中的公共字段使用相同字段位置的引用。

3.7. 自定义输出

在过去的示例中,我们已经看到join如何打印第一个文件的记录,然后打印第二个文件中的相应记录。通过-o选项,我们能够为join*提供自定义输出格式以供使用*:

$ join -t , -o 1.1,2.2,1.2 device_names_comma.log device_policies_comma.log
10.0.1.10,RED,WINSHARE01
10.0.1.13,YELLOW,WEBSERVER03
10.0.1.15,RED,FINSERVER02
143.204.74.129,YELLOW,WEBSERVER02
152.120.106.21,BLUE,HRSERVER01
192.168.8.28,GREEN,MYWORKSTATION
192.168.10.4,RED,PRINTER02

通过使用file_number.field_number的符号,我们能够定义我们希望输出呈现给我们的方式。我们将参数列表中的第一个文件引用为1并将第二个文件引用为2。我们的字段编号从 1 开始。我们选择的分隔符分隔输出。