如何从 Bash 中的数组中获取唯一值?
- 2024-10-23 08:47:00
- admin 原创
- 68
问题描述:
我有与这里几乎相同的问题。
我有一个包含aa ab aa ac aa ad
等的数组。现在我想从这个数组中选择所有唯一元素。我想,这会很简单sort | uniq
,或者就像sort -u
他们在另一个问题中提到的那样,但数组中没有任何变化……代码是:
echo `echo "${ids[@]}" | sort | uniq`
我做错什么了?
解决方案 1:
有点不靠谱,但是应该可以做到:
echo "${ids[@]}" | tr ' ' '
' | sort -u | tr '
' ' '
要将排序的唯一结果保存回数组,请执行数组赋值:
sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '
' | sort -u | tr '
' ' '))
如果你的 shell 支持这里的字符串(bash
应该),你可以echo
通过将其更改为节省进程:
tr ' ' '
' <<< "${ids[@]}" | sort -u | tr '
' ' '
截至 2021 年 8 月 28 日的说明:
根据ShellCheck wiki 2207,应使用管道read -a
来避免拆分。因此,在 bash 中,命令将是:
`IFS=" " read -r -a ids <<< "$(echo "${ids[@]}" | tr ' ' '
' | sort -u | tr '
' ' ')"`
或者
`IFS=" " read -r -a ids <<< "$(tr ' ' '
' <<< "${ids[@]}" | sort -u | tr '
' ' ')"`
输入:
ids=(aa ab aa ac aa ad)
输出:
aa ab ac ad
解释:
"${ids[@]}"
- 使用 shell 数组的语法,无论用作部分echo
还是字符串。@
部分表示“数组中的所有元素”`tr ' ' '
'`- 将所有空格转换为换行符。因为 shell 将数组视为一行上的元素,用空格分隔;并且 sort 要求输入位于不同的行上。sort -u
- 排序并仅保留唯一元素`tr '
' ' '`- 将我们之前添加的换行符转换回空格。$(...)
-命令替换除此之外:`tr ' ' '
' <<< "${ids[@]}"还有一种更有效的方式:
echo "${ids[@]}" | tr ' ' '
'`
解决方案 2:
如果您运行的是 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
' "${!b[@]}"
ac ad
ac
aa
ad
这是可行的,因为在任何数组(关联或传统,任何语言)中,每个键只能出现一次。当循环到达中for
的第二个值时,它会覆盖 的原始设置。aa
`a[2]b[aa]
a[0]`
sort
在本机 bash 中执行操作比使用管道和外部工具(如和)更快uniq
,但对于更大的数据集,如果使用更强大的语言(如 awk、python 等),您可能会看到更好的性能。
如果你有信心,你可以for
使用printf
来循环使用多个参数的格式,从而避免循环,尽管这似乎需要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 或 greycat 不会发现您的代码有任何错误,请改用 for 循环。
解决方案 3:
我意识到这个问题已经得到解答了,但是它在搜索结果中排名很高,可能会对某些人有所帮助。
printf "%s
" "${IDS[@]}" | sort -u
例子:
~> IDS=( "aa" "ab" "aa" "ac" "aa" "ad" )
~> echo "${IDS[@]}"
aa ab aa ac aa ad
~>
~> printf "%s
" "${IDS[@]}" | sort -u
aa
ab
ac
ad
~> UNIQ_IDS=($(printf "%s
" "${IDS[@]}" | sort -u))
~> echo "${UNIQ_IDS[@]}"
aa ab ac ad
~>
解决方案 4:
如果数组元素包含空格或任何其他 shell 特殊字符(您能确定它们没有吗?),那么要首先捕获这些字符(您应该始终这样做)用双引号表示数组!例如"${a[@]}"
。Bash 会将其逐字解释为“每个数组元素都在单独的参数中”。在 bash 中,这始终有效,始终有效。
然后,为了得到一个排序的(且唯一的)数组,我们必须将其转换为 sort 可以理解的格式,并能够将其转换回 bash 数组元素。这是我想到的最好的办法:
eval a=($(printf "%q
" "${a[@]}" | sort -u))
不幸的是,在空数组的特殊情况下,这会失败,将空数组变成一个包含 1 个空元素的数组(因为 printf 有 0 个参数,但仍然打印为好像它有一个空参数 - 请参阅解释)。所以你必须在 if 或其他东西中捕获它。
解释:printf 的 %q 格式“shell 转义”打印的参数,就像 bash 可以在 eval 之类的程序中恢复一样!因为每个元素都是在其自己的行上打印的 shell 转义,所以元素之间的唯一分隔符是换行符,并且数组赋值将每一行作为一个元素,将转义的值解析为文字。
例如
> a=("foo bar" baz)
> printf "%q
" "${a[@]}"
'foo bar'
baz
> printf "%q
"
''
为了去除返回数组中的每个值的转义,eval 是必需的。
解决方案 5:
'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` )
解决方案 6:
这一个也将保留秩序:
echo ${ARRAY[@]} | tr [:space:] '
' | awk '!a[$0]++'
并使用唯一值修改原始数组:
ARRAY=($(echo ${ARRAY[@]} | tr [:space:] '
' | awk '!a[$0]++'))
解决方案 7:
要创建由唯一值组成的新数组,请确保数组不为空,然后执行以下操作之一:
删除重复条目(带排序)
readarray -t NewArray < <(printf '%s
' "${OriginalArray[@]}" | sort -u)
删除重复条目(不排序)
readarray -t NewArray < <(printf '%s
' "${OriginalArray[@]}" | awk '!x[$0]++')
警告:不要尝试做类似的事情`NewArray=( $(printf '%s
' "${OriginalArray[@]}" | sort -u) )`。它会在空格处中断。
解决方案 8:
这个变化怎么样?
printf '%s
' "${ids[@]}" | sort -u
解决方案 9:
在不丢失原始顺序的情况下:
uniques=($(tr ' ' '
' <<<"${original[@]}" | awk '!u[$0]++' | tr '
' ' '))
解决方案 10:
如果您想要一个仅使用 bash 内部的解决方案,您可以将值设置为关联数组中的键,然后提取键:
declare -A uniqs
list=(foo bar bar "bar none")
for f in "${list[@]}"; do
uniqs["${f}"]=""
done
for thing in "${!uniqs[@]}"; do
echo "${thing}"
done
这将输出
bar
foo
bar none
解决方案 11:
猫号.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
仅查找 Uniq 记录:
cat number.txt | awk '{for(i=1;i<=NF;i++) print $i|"sort|uniq -u"}
1
5
6
解决方案 12:
处理嵌入空格的另一种方法是用 进行空分隔printf
,用 进行区分sort
,然后使用循环将其重新打包到数组中:
input=(a b c "$(printf "d
e")" b c "$(printf "d
e")")
output=()
while read -rd $'' element
do
output+=("$element")
done < <(printf "%s " "${input[@]}" | sort -uz)
在此结束时,input
包含output
所需的值(提供的顺序并不重要):
$ printf "%q
" "${input[@]}"
a
b
c
$'d
e'
b
c
$'d
e'
$ printf "%q
" "${output[@]}"
a
b
c
$'d
e'
解决方案 13:
以下所有工作在bash
和sh
中都没有错误shellcheck
,但您需要抑制SC2207
arrOrig=("192.168.3.4" "192.168.3.4" "192.168.3.3")
# NO SORTING
# shellcheck disable=SC2207
arr1=($(tr ' ' '
' <<<"${arrOrig[@]}" | awk '!u[$0]++' | tr '
' ' ')) # @estani
len1=${#arr1[@]}
echo "${len1}"
echo "${arr1[*]}"
# SORTING
# shellcheck disable=SC2207
arr2=($(printf '%s
' "${arrOrig[@]}" | sort -u)) # @das.cyklone
len2=${#arr2[@]}
echo "${len2}"
echo "${arr2[*]}"
# SORTING
# shellcheck disable=SC2207
arr3=($(echo "${arrOrig[@]}" | tr ' ' '
' | sort -u | tr '
' ' ')) # @sampson-chen
len3=${#arr3[@]}
echo "${len3}"
echo "${arr3[*]}"
# SORTING
# shellcheck disable=SC2207
arr4=($(for i in "${arrOrig[@]}"; do echo "${i}"; done | sort -u)) # @corbyn42
len4=${#arr4[@]}
echo "${len4}"
echo "${arr4[*]}"
# NO SORTING
# shellcheck disable=SC2207
arr5=($(echo "${arrOrig[@]}" | tr "[:space:]" '
' | awk '!a[$0]++')) # @faustus
len5=${#arr5[@]}
echo "${len5}"
echo "${arr5[*]}"
# OUTPUTS
# arr1
2 # length
192.168.3.4 192.168.3.3 # items
# arr2
2 # length
192.168.3.3 192.168.3.4 # items
# arr3
2 # length
192.168.3.3 192.168.3.4 # items
# arr4
2 # length
192.168.3.3 192.168.3.4 # items
# arr5
2 # length
192.168.3.4 192.168.3.3 # items
所有这些的输出都是 2,并且正确。这个答案基本上总结并整理了这篇文章中的其他答案,是一个有用的快速参考。给出了原始答案的归属。
解决方案 14:
在 zsh 中你可以使用 (u) 标志:
$ ids=(aa ab aa ac aa ad)
$ print ${(u)ids}
aa ab ac ad
解决方案 15:
尝试此操作以获取文件中第一列的唯一值
awk -F, '{a[$1];}END{for (i in a)print i;}'
解决方案 16:
BASH 单行,不改变顺序,并且包含带空格的项目:
readarray -t my_array < <( (for i in "${my_array[@]}"; do echo "$i"; done) | awk '!uniq[$0]++' )
解决方案 17:
# Read a file into variable
lines=$(cat /path/to/my/file)
# Go through each line the file put in the variable, and assign it a variable called $line
for line in $lines; do
# Print the line
echo $line
# End the loop, then sort it (add -u to have unique lines)
done | sort -u
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件