如何在 bash 中从字符串/数组创建唯一元素的数组?

如何在 bash 中从字符串/数组创建唯一元素的数组?

如果我有一个字符串“1 2 3 2 1” - 或一个数组 [1,2,3,2,1] - 如何选择唯一值,即

"1 2 3 2 1" produces "1 2 3" 

或者

[1,2,3,2,1] produces [1,2,3]

与 uniq 类似,但 uniq 似乎适用于整行,而不是行内的模式......

答案1

如果您使用的是 zsh:

$ array=(1 2 3 2 1)
$ echo ${(u)array[@]}
1 2 3

或者(如果KSH_ARRAYS未设置选项)甚至

$ echo ${(u)array}
1 2 3

答案2

使用 GNU awk(这也保留原始顺序)

printf '%s\n' "1 2 3 2 1" | awk -v RS='[[:space:]]+' '!a[$0]++{printf "%s%s", $0, RT}'
1 2 3 

read一个bash数组中

read -ra arr<<<$(printf '%s\n' "1 2 3 2 1" |
 awk -v RS='[[:space:]]+' '!a[$0]++{printf "%s%s", $0, RT}')
printf "%s\n"  "${arr[@]}"
1
2
3

答案3

对于具有任意值的数组,这是相当棘手的,bash因为它没有内置的运算符。

bash但是碰巧不支持在其变量中存储 NUL 字符,因此您可以利用它将其传递给其他命令:

相当于zsh的:

new_array=("${(@u}array}")

在最近的 GNU 系统上,可能是:

eval "new_array=($(
  printf "%s\0" "${array[@]}" |
    LC_ALL=C sort -zu |
    xargs -r0 bash -c 'printf "%q\n" "$@"' sh
  ))"

或者,使用最新版本的bash,并假设所有数组元素都不为空,您可以使用关联数组:

unset hash
typeset -A hash
for i in "${array[@]}"; do
  hash[$i]=
done
new_array=("${!hash[@]}")

使用 bash 4.4 及更新版本和 GNU sort

readarray -td '' new_array < <(
  printf '%s\0' "${array[@]}" | LC_ALL=C sort -zu)

在这些不同的解决方案中,元素的顺序不会相同。

tcsh

set -f new_array = ($array:q)

会保留F第一个元素 ( a b a=> a b) 就像zsh(u)扩展标志。

set -l new_array = ($array:q)

将保留最后一个 ( a b a=> b a)。然而,它们从数组中删除空元素。

答案4

要完全在 shell 中完成并将结果放入数组中,

declare -A seen
for word in one two three two one
do
        if [ ! "${seen[$word]}" ]
        then
                result+=("$word")
                seen[$word]=1
        fi
done
echo "${result[@]}"

换句话说:如果我们还没有看到给定的单词,请将其添加到result数组中并将其标记为已看到。一旦看到某个单词,就忽略它随后出现的情况。

相关内容