Contents

从bash中的数组中获取唯一的值

1. 概述

Bash 是安装在几乎所有 Linux 系统上的多功能 shell。

除了作为交互式 shell 的价值之外,bash 还可以用于自动执行各种任务。这些任务通常会处理我们的数据列表:例如,文件列表或 IP 地址列表。

我们可以将这些列表组织成数组 。Bash 使用索引数组(我们通过编号引用项目)和关联数组(我们通过名称引用项目)。关联数组在其他编程语言中通常称为映射或字典。

在本教程中,我们将一些列表分配给数组。然后,我们将研究从数组中筛选出重复项的方法,因此它们只包含唯一条目而没有重复项。

2. 声明和使用数组

Bash Arrays 在本网站的其他地方进行了深入介绍。

简而言之,我们需要知道如何声明这两种数组(索引数组和关联数组)。我们还需要知道如何引用整个数组。

我们可以使用括号定义索引数组:

ip_addrs=(192.168.2.101 192.168.2.105 192.168.2.201 192.168.2.110)

要使用整个数组而不是单个索引,我们使用 @ 符号而不是数字。

这让我们可以循环遍历数组中的所有元素,而无需跟踪索引。例如,假设我们想对每个地址执行一次 ping 操作:

for ip in ${ip_addrs[@]}; do
    ping -D -c1 $ip
done

2.1.关联数组

对于某些数据,我们想要的不仅仅是一个编号列表。关联数组让我们为数据(“值”)命名(“键”)。NoSQL 数据库也经常以这种方式组织数据。

我们可以像这样一次分配几个键/值对:

declare -A ips_by_hostname=([mysql]=192.168.2.101 [nginx]=192.168.2.99 [smtp]=192.168.2.105)

让我们记住首先使用declare -A将变量“声明”为关联数组。

我们还可以生成一个包含所有值或所有键的数组

要列出所有关联数组的值,我们使用与索引数组类似的语法:

$ echo "${ips_by_hostname[@]}"
192.168.2.101 192.168.2.105 192.168.2.99

要查看键,请在数组变量名称前添加一个感叹号:

for host in "${!ips_by_hostname[@]}"; do
    echo "key: $host value: ${ips_by_hostname[$host]}"
done
key: mysql value: 192.168.2.101
key: smtp value: 192.168.2.99
key: nginx value: 192.168.2.105

3. 只有独特的价值

那么让我们回到我们最初的问题:当我们的数组包含重复项时,有什么简单的方法可以删除它们?

3.1. 分配给关联数组键

关联数组的键对于该数组必须始终是唯一的。一键,一值。

我们可以利用它来发挥我们的优势。如果我们遍历一个数组并将每个项目分配给关联数组的键,则该数组的键将是原始数组的一组唯一值:

declare -a ip_addrs=(192.168.1.101 192.168.1.105 192.168.1.105 192.168.1.106)
declare -A uniq_tmp
for ip in "${ip_addrs[@]}"; do
    uniq_tmp[$ip]=0 # assigning a placeholder
done
echo "unique: ${!uniq_tmp[@]}" # only the keys

这将输出三个 IP 地址,省略重复的 192.168.1.105。该解决方案的优点是全部在 bash 中工作,而不是运行任何其他程序。

3.2. 使用sort -u

在编写 shell 脚本时,我们还有其他选择。我们可以通过GNUsort实用程序 传递我们的数组。

这将改变项目的顺序,所以如果它在我们的特定脚本中很重要,请记住这一点。

sort命令逐行对输入进行排序,因此我们需要将数组拆分为多行。

sort命令的*-u*选项会丢弃任何重复的行。

所以我们可以像这样使用我们的 IP 地址数组:

uniqs_arr=($(for ip in "${ip_addrs[@]}"; do echo "${ip}"; done | sort -u))

现在我们有一个唯一的排序值数组。

3.3. 使用trawk

一旦我们开始使用其余的 Unix 工具集合,我们就会开始到处寻找解决方案。除了 bash 之外,一个强大的工具是awk

uniqs_arr=($(tr ' ' '\n' <<<"${ip_addrs[@]}" | awk '!u[$0]++' | tr '\n' ' '))

这进入了更复杂的领域。但要分解它:

  1. 我们需要再次将数组分成单独的行。这一次,我们使用tr 将项目之间的空格转换为换行符 (\n)。
  2. 我们通过管道将其传递给awk,并让它只引用唯一的行。
  3. 然后我们再次将输出转换回数组,再次使用tr

这种方法的优点是我们保留了数组元素的顺序。