如果我有一个字符串“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
数组中并将其标记为已看到。一旦看到某个单词,就忽略它随后出现的情况。