我有几乎和 这里 一样的问题。
我有一个包含 aa ab aa ac aa ad
等的数组。现在我想从这个数组中选择所有唯一元素。想,这很简单 sort | uniq
或 sort -u
正如他们在另一个问题中提到的那样,但数组中没有任何变化……代码是:
echo `echo "${ids[@]}" | sort | uniq`
我究竟做错了什么?
原文由 Jetse 发布,翻译遵循 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 中执行操作可能比使用管道和外部工具(如 sort
和 uniq
)更快,但对于更大的数据集,如果您使用更强大的语言,例如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 许可协议
7 回答5.3k 阅读
4 回答4k 阅读
2 回答5.9k 阅读✓ 已解决
2 回答2.5k 阅读✓ 已解决
1 回答2.3k 阅读✓ 已解决
2 回答795 阅读✓ 已解决
2 回答3.2k 阅读
有点hacky,但应该这样做:
要将排序后的唯一结果保存回数组中,请执行 Array assignment :
如果你的shell支持 herestrings (
bash
echo
),你可以通过将其更改为:截至 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[@]}"
- 用于处理 shell 数组的语法,无论是用作echo
的一部分还是一个字符串。@
部分表示“数组中的所有元素”tr ' ' '\n'
- 将所有空格转换为换行符。因为你的数组被shell看作是一行中的元素,用空格分隔;并且因为 sort 期望输入在不同的行上。sort -u
- 仅排序和保留唯一元素tr '\n' ' '
将我们之前添加的换行符转换回空格。$(...)
- 命令替换tr ' ' '\n' <<< "${ids[@]}"
是一种更有效的做法:echo "${ids[@]}" | tr ' ' '\n'