如何从 Bash 中的数组中获取唯一值?

新手上路,请多包涵

我有几乎和 这里 一样的问题。

我有一个包含 aa ab aa ac aa ad 等的数组。现在我想从这个数组中选择所有唯一元素。想,这很简单 sort | uniqsort -u 正如他们在另一个问题中提到的那样,但数组中没有任何变化……代码是:

 echo `echo "${ids[@]}" | sort | uniq`

我究竟做错了什么?

原文由 Jetse 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 692
2 个回答

有点hacky,但应该这样做:

 echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '

要将排序后的唯一结果保存回数组中,请执行 Array assignment

 sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))

如果你的shell支持 herestringsbash echo ),你可以通过将其更改为:

 tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' '

截至 2021 年 8 月 28 日的说明:

根据 ShellCheck wiki 2207 a read -a 应使用管道以避免分裂。因此,在 bash 中,命令将是:

IFS=" " read -r -a ids <<< "$(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')"

或者

IFS=" " read -r -a ids <<< "$(tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' ')"

输入:

 ids=(aa ab aa ac aa ad)

输出:

 aa ab ac ad

解释:

  • "${ids[@]}" - 用于处理 shell 数组的语法,无论是用作 echo 的一部分还是一个字符串。 @ 部分表示“数组中的所有元素”
  • tr ' ' '\n' - 将所有空格转换为换行符。因为你的数组被shell看作是一行中的元素,用空格分隔;并且因为 sort 期望输入在不同的行上。
  • sort -u - 仅排序和保留唯一元素
  • tr '\n' ' ' 将我们之前添加的换行符转换回空格。
  • $(...) - 命令替换
  • 旁白: tr ' ' '\n' <<< "${ids[@]}" 是一种更有效的做法: echo "${ids[@]}" | tr ' ' '\n'

原文由 sampson-chen 发布,翻译遵循 CC BY-SA 4.0 许可协议

如果您正在运行 Bash 版本 4 或更高版本(在任何现代版本的 Linux 中都应该是这种情况),您可以通过创建一个包含原始数组的每个值的新关联数组来在 bash 中获取唯一的数组值。像这样的东西:

 $ a=(aa ac aa ad "ac ad")
$ declare -A b
$ for i in "${a[@]}"; do b["$i"]=1; done
$ printf '%s\n' "${!b[@]}"
ac ad
ac
aa
ad

这是有效的,因为在任何数组(关联或传统,任何语言)中,每个键只能出现一次。 When the for loop arrives at the second value of aa in a[2] , it overwrites b[aa] which was set originally for a[0] .

在本机 bash 中执行操作可能比使用管道和外部工具(如 sortuniq )更快,但对于更大的数据集,如果您使用更强大的语言,例如awk、python 等

如果您有信心,可以通过使用 printf 为多个参数回收其格式的能力来避免 for 循环,尽管这似乎需要 eval 。 (如果您对此感到满意,请立即停止阅读。)

 $ eval b=( $(printf ' ["%s"]=1' "${a[@]}") )
$ declare -p b
declare -A b=(["ac ad"]="1" [ac]="1" [aa]="1" [ad]="1" )

此解决方案需要 eval 的原因是数组值是在分词之前确定的。这意味着命令替换的输出被认为 是一个单词 而不是一组键=值对。

虽然这使用了一个子shell,但它只使用 bash 内置函数来处理数组值。请务必以批判的眼光评估您对 eval 的使用。如果您不是 100% 确信 chepner 或 glenn jackman 或 graycat 不会发现您的代码有问题,请改用 for 循环。

原文由 ghoti 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题