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


8

如果我有一个字符串“ 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似乎可以在整行上运行,而不是行内的模式...

Answers:


4

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

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

readbash数组

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

然后如何使该数组成为数组?
Michael Durrant 2014年

@MichaelDurrant,如果您要表示bash数组,则添加了一种方法
iruvar

看看这里您的数组是否包含空格
Tom Hale

@iruvar您能否解释一下这实际上意味着什么?我是awk脚本的新手,如果您可以说出这句话的确切含义,这将很有帮助!a [$ 0] ++
Abhishek

如果无法在评论中解释@iruvar,则任何解释上述语法的网站至少都是有益的。
阿比舍克

9

如果您使用的是zsh:

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

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

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

1
如果数组可能包含空元素,则应使用"${(u)array[@]}""${(@u)array}"代替(请注意引号)。
斯特凡Chazelas

我正在使用zsh 5.1.1(x86_64-ubuntu-linux-gnu)${(u)array}即使该数组为空或包含空字符串(不带引号)也可以使用。
kiamlaluno

4

对于具有任意值的数组,使用它非常棘手,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 IRST元件(a b a=> a b)等zsh(u)扩展标志位。

set -l new_array = ($array:q)

将保留最后一个(a b a=> b a)。但是那些从数组中删除了空元素。


1

这个解决方案对我有用。

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

上面产生1 2 3作为输出。

如Costas所建议的,较短的版本可能是

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

要将最终结果存储到数组中,您可以执行以下操作:

IFS=$' '
arr=($(printf "%s\n" "${ids[@]}" | sort -u | tr '\n' ' '))
unset IFS

现在,当我对进行回显时arr,这就是我得到的输出。

echo "${arr[@]}"
1 2 3

参考文献

https://stackoverflow.com/a/13648438/1742825 https://stackoverflow.com/a/9449633/1742825


@Costas,谢谢。我已经将其纳入答案。
拉梅什2014年

如何使最终结果成为数组?
Michael Durrant 2014年

@MichaelDurrant,请参阅更新后的答案,并让我知道是否可以。
拉梅什2014年

如果要将结果放入数组中,则可以删除最后一个命令tr '\n' ' '
Costas 2014年

0

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

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数组中并将其标记为已看到。一旦看到一个单词,就忽略它的后续出现。


2
请注意,如果unset seen之前已定义(即使是环境中的标量变量),则需要先declare -A seen进行准备$seen
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.