我有一个包含aa ab aa ac aa ad
等元素的数组。现在我想从这个数组中选择所有唯一的元素。尽管在那个问题中提到可以使用sort | uniq
或sort -u
来简单地实现,但是数组中没有任何变化... 代码如下:
我几乎和here一样有同样的问题。
echo `echo "${ids[@]}" | sort | uniq`
我做错了什么?
echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '
sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
bash
应该支持),你可以通过将其改为以下内容来节省一个echo
进程:tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' '
2021年8月28日更新:
根据ShellCheck wiki 2207,应使用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
还是 here 字符串的一部分。@
部分表示“数组中的所有元素”tr ' ' '\n'
- 将所有空格转换为换行符。因为 shell 看到的数组是由空格分隔的单行元素;而 sort 命令期望输入在不同的行上。sort -u
- 排序并保留唯一元素tr '\n' ' '
- 将之前添加的换行符转换回空格。$(...)
- 命令替换tr ' ' '\n' <<< "${ids[@]}"
是更有效的方法:echo "${ids[@]}" | tr ' ' '\n'
如果您正在运行 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
这种方法可行是因为在任何数组(无论是关联数组还是普通数组,在任何语言中)中,每个键只能出现一次。当for
循环到a[2]
中的第二个值aa
时,它覆盖了最初为a[0]
设置的b[aa]
。
在本机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
的使用。如果您不百分之百确信chepner、glenn jackman或greycat不会对您的代码有任何问题,请使用for循环。
$ bash --version GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin14) Copyright (C) 2007 Free Software Foundation, Inc.
尽管我最喜欢这个答案,但它存在可移植性问题。 - nhed我知道这个问题已经有了答案,但它在搜索结果中排名很高,可能会对某些人有所帮助。
printf "%s\n" "${IDS[@]}" | sort -u
例子:
~> IDS=( "aa" "ab" "aa" "ac" "aa" "ad" )
~> echo "${IDS[@]}"
aa ab aa ac aa ad
~>
~> printf "%s\n" "${IDS[@]}" | sort -u
aa
ab
ac
ad
~> UNIQ_IDS=($(printf "%s\n" "${IDS[@]}" | sort -u))
~> echo "${UNIQ_IDS[@]}"
aa ab ac ad
~>
ids=(ab "a a" ac aa ad ac aa);IFS=$'\n' ids2=(\
printf "%s\n" "${ids[@]}" |sort -u`),所以我添加了
IFS=$'\n'`,正如 @gniourf_gniourf 建议的那样。 - Aquarius PowerIFS=$'\n'; ids2=(...)
,因为在变量赋值之前临时赋值是不可能的。相反,请使用这个结构:IFS=$'\n' read -r -a ids2 <<<"$(printf "%s\n" "${ids[@]}" | sort -u)"
。 - Yeti如果您的数组元素包含空格或其他shell特殊字符(您能确定它们没有吗?),那么首先要捕获这些字符(而且您应该总是这样做),请用双引号表达您的数组!例如:"${a[@]}"
。Bash会把它解释为“每个数组元素都是单独的参数”。在bash中,这始终有效。
然后,为了获得已排序(并且唯一)的数组,我们必须将其转换为sort理解的格式,并能够将其转换回bash数组元素。这是我想出的最好方法:
eval a=($(printf "%q\n" "${a[@]}" | sort -u))
很不幸,这在空数组的特殊情况下失败了,将空数组转换为一个由1个空元素组成的数组(因为printf没有参数但仍然像有一个空参数一样打印 - 请参见解释)。所以你必须在if语句或其他地方捕捉它。> a=("foo bar" baz)
> printf "%q\n" "${a[@]}"
'foo bar'
baz
> printf "%q\n"
''
eval是必要的,以便从返回数组中的每个值中去除转义。
uniq
而不是 sort -u
。 - Jesse Chisholmuniq
在未排序的列表上无法正常工作,因此必须始终与 sort
结合使用。 - Jean Paul%q
而不是%s
吗? - Changdae Park'sort'可以用于对for循环的输出进行排序:
for i in ${ids[@]}; do echo $i; done | sort
使用"-u"来消除重复项:
for i in ${ids[@]}; do echo $i; done | sort -u
最后,您可以使用唯一元素覆盖数组:
ids=( `for i in ${ids[@]}; do echo $i; done | sort -u` )
ids=( \
for i in ${ids[@]}; do echo $i; done | uniq` )` - Jesse Chisholmuniq
仅删除相邻的重复行。 - Jason Kohles这个也将保留顺序:
echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[$0]++'
并使用唯一值修改原始数组:
ARRAY=($(echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[$0]++'))
uniq
。它需要排序,而awk不需要,此答案的目的是在输入未排序时保留顺序。 - bukzorreadarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | sort -u)
readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | awk '!x[$0]++')
警告:不要尝试像NewArray=( $(printf '%s\n' "${OriginalArray[@]}" | sort -u) )
这样做。因为空格会导致出错。
uniq
只合并相邻的重复行,因此它与 awk '!x[$0]++'
不同。 - Sixprintf '%s\n' "${ids[@]}" | sort -u
sorted_arr=($(printf '%s\n' "${ids[@]}" | sort -u)
。 - algae在不失去原始顺序的情况下:
uniques=($(tr ' ' '\n' <<<"${original[@]}" | awk '!u[$0]++' | tr '\n' ' '))
cat number.txt
1 2 3 4 4 3 2 5 6
cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}'
1
2
3
4
4
3
2
5
6
cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk 'x[$0]++'
4
3
2
cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk '!x[$0]++'
1
2
3
4
5
6
查找唯一的记录:
cat number.txt | awk '{for(i=1;i<=NF;i++) print $i|"sort|uniq -u"}
1
5
6
uniq=($(printf "%s\n" "${ids[@]}" | sort -u)); echo "${uniq[@]}"
- glenn jackmansorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
。如果没有额外的括号,它会将其作为字符串处理。 - whla... | uniq | ...
代替... | sort -u | ...
。 - Jesse Chisholm