如何从 Bash 中的数组中获取唯一值?

2024-10-23 08:47:00
admin
原创
68
摘要:问题描述:我有与这里几乎相同的问题。我有一个包含aa ab aa ac aa ad等的数组。现在我想从这个数组中选择所有唯一元素。我想,这会很简单sort | uniq,或者就像sort -u他们在另一个问题中提到的那样,但数组中没有任何变化……代码是:echo `echo "${ids[@...

问题描述:

我有与这里几乎相同的问题。

我有一个包含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

解释:

  • &quot;${ids[@]}&quot;- 使用 shell 数组的语法,无论用作部分echo还是字符串。@部分表示“数组中的所有元素”

  • `tr ' ' '
    '`- 将所有空格转换为换行符。因为 shell 将数组视为一行上的元素,用空格分隔;并且 sort 要求输入位于不同的行上。

  • sort -u- 排序并仅保留唯一元素

  • `tr '
    ' ' '`- 将我们之前添加的换行符转换回空格。

  • $(...)-命令替换

  • 除此之外:`tr ' ' '
    ' <<< "${ids[@]}"还有一种更有效的方式:echo "${ids[@]}" | tr ' ' '

'`

解决方案 2:

如果您运行的是 Bash 版本 4 或更高版本(任何现代版本的 Linux 都应该如此),您可以通过创建一个包含原始数组的每个值的新关联数组来在 bash 中获取唯一的数组值。如下所示:

$ a=(aa ac aa ad &quot;ac ad&quot;)
$ declare -A b
$ for i in &quot;${a[@]}&quot;; do b[&quot;$i&quot;]=1; done
$ printf &#039;%s
&#039; &quot;${!b[@]}&quot;
ac ad
ac
aa
ad

这是可行的,因为在任何数组(关联或传统,任何语言)中,每个键只能出现一次。当循环到达中for的第二个值时,它会覆盖 的原始设置。aa`a[2]b[aa]a[0]`

sort在本机 bash 中执行操作比使用管道和外部工具(如和)更快uniq,但对于更大的数据集,如果使用更强大的语言(如 awk、python 等),您可能会看到更好的性能。

如果你有信心,你可以for使用printf来循环使用多个参数的格式,从而避免循环,尽管这似乎需要eval。 (如果你对此没意见,请立即停止阅读。)

$ eval b=( $(printf &#039; [&quot;%s&quot;]=1&#039; &quot;${a[@]}&quot;) )
$ declare -p b
declare -A b=([&quot;ac ad&quot;]=&quot;1&quot; [ac]=&quot;1&quot; [aa]=&quot;1&quot; [ad]=&quot;1&quot; )

此解决方案需要的原因eval是数组值在分词之前确定。这意味着命令替换的输出被视为单个单词,而不是一组键=值对。

虽然这使用了子 shell,但它仅使用 bash 内置函数来处理数组值。请务必eval以批判的眼光评估您的使用。如果您不能 100% 确信 chepner 或 glenn jackman 或 greycat 不会发现您的代码有任何错误,请改用 for 循环。

解决方案 3:

我意识到这个问题已经得到解答了,但是它在搜索结果中排名很高,可能会对某些人有所帮助。

printf &quot;%s
&quot; &quot;${IDS[@]}&quot; | sort -u

例子:

~> IDS=( &quot;aa&quot; &quot;ab&quot; &quot;aa&quot; &quot;ac&quot; &quot;aa&quot; &quot;ad&quot; )
~> echo  &quot;${IDS[@]}&quot;
aa ab aa ac aa ad
~>
~> printf &quot;%s
&quot; &quot;${IDS[@]}&quot; | sort -u
aa
ab
ac
ad
~> UNIQ_IDS=($(printf &quot;%s
&quot; &quot;${IDS[@]}&quot; | sort -u))
~> echo &quot;${UNIQ_IDS[@]}&quot;
aa ab ac ad
~>

解决方案 4:

如果数组元素包含空格或任何其他 shell 特殊字符(您能确定它们没有吗?),那么要首先捕获这些字符(您应该始终这样做)用双引号表示数组!例如&quot;${a[@]}&quot;。Bash 会将其逐字解释为“每个数组元素都在单独的参数中”。在 bash 中,这始终有效,始终有效。

然后,为了得到一个排序的(且唯一的)数组,我们必须将其转换为 sort 可以理解的格式,并能够将其转换回 bash 数组元素。这是我想到的最好的办法:

eval a=($(printf &quot;%q
&quot; &quot;${a[@]}&quot; | sort -u))

不幸的是,在空数组的特殊情况下,这会失败,将空数组变成一个包含 1 个空元素的数组(因为 printf 有 0 个参数,但仍然打印为好像它有一个空参数 - 请参阅解释)。所以你必须在 if 或其他东西中捕获它。

解释:printf 的 %q 格式“shell 转义”打印的参数,就像 bash 可以在 eval 之类的程序中恢复一样!因为每个元素都是在其自己的行上打印的 shell 转义,所以元素之间的唯一分隔符是换行符,并且数组赋值将每一行作为一个元素,将转义的值解析为文字。

例如

> a=(&quot;foo bar&quot; baz)
> printf &quot;%q
&quot; &quot;${a[@]}&quot;
&#039;foo bar&#039;
baz
> printf &quot;%q
&quot;
&#039;&#039;

为了去除返回数组中的每个值的转义,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:] &#039;
&#039; | awk &#039;!a[$0]++&#039;

并使用唯一值修改原始数组:

ARRAY=($(echo ${ARRAY[@]} | tr [:space:] &#039;
&#039; | awk &#039;!a[$0]++&#039;))

解决方案 7:

要创建由唯一值组成的新数组,请确保数组不为空,然后执行以下操作之一:

删除重复条目(带排序)

readarray -t NewArray &lt; &lt;(printf &#039;%s
&#039; &quot;${OriginalArray[@]}&quot; | sort -u)

删除重复条目(不排序)

readarray -t NewArray &lt; &lt;(printf &#039;%s
&#039; &quot;${OriginalArray[@]}&quot; | awk &#039;!x[$0]++&#039;)

警告:不要尝试做类似的事情`NewArray=( $(printf '%s
' "${OriginalArray[@]}" | sort -u) )`。它会在空格处中断。

解决方案 8:

这个变化怎么样?

printf &#039;%s
&#039; &quot;${ids[@]}&quot; | sort -u

解决方案 9:

在不丢失原始顺序的情况下:

uniques=($(tr &#039; &#039; &#039;
&#039; &lt;&lt;&lt;&quot;${original[@]}&quot; | awk &#039;!u[$0]++&#039; | tr &#039;
&#039; &#039; &#039;))

解决方案 10:

如果您想要一个仅使用 bash 内部的解决方案,您可以将值设置为关联数组中的键,然后提取键:

declare -A uniqs
list=(foo bar bar &quot;bar none&quot;)
for f in &quot;${list[@]}&quot;; do 
  uniqs[&quot;${f}&quot;]=&quot;&quot;
done

for thing in &quot;${!uniqs[@]}&quot;; do
  echo &quot;${thing}&quot;
done

这将输出

bar
foo
bar none

解决方案 11:

猫号.txt

1 2 3 4 4 3 2 5 6

将行打印到列中:cat number.txt | awk &#039;{for(i=1;i&lt;=NF;i++) print $i}&#039;

1
2
3
4
4
3
2
5
6

查找重复的记录:cat number.txt | awk &#039;{for(i=1;i&lt;=NF;i++) print $i}&#039; |awk &#039;x[$0]++&#039;

4
3
2

替换重复记录:cat number.txt | awk &#039;{for(i=1;i&lt;=NF;i++) print $i}&#039; |awk &#039;!x[$0]++&#039;

1
2
3
4
5
6

仅查找 Uniq 记录: cat number.txt | awk &#039;{for(i=1;i&lt;=NF;i++) print $i|&quot;sort|uniq -u&quot;}

1
5
6

解决方案 12:

处理嵌入空格的另一种方法是用 进行空分隔printf,用 进行区分sort,然后使用循环将其重新打包到数组中:

input=(a b c &quot;$(printf &quot;d
e&quot;)&quot; b c &quot;$(printf &quot;d
e&quot;)&quot;)
output=()

while read -rd $&#039;&#039; element
do 
  output+=(&quot;$element&quot;)
done &lt; &lt;(printf &quot;%s&quot; &quot;${input[@]}&quot; | sort -uz)

在此结束时,input包含output所需的值(提供的顺序并不重要):

$ printf &quot;%q
&quot; &quot;${input[@]}&quot;
a
b
c
$&#039;d
e&#039;
b
c
$&#039;d
e&#039;

$ printf &quot;%q
&quot; &quot;${output[@]}&quot;
a
b
c
$&#039;d
e&#039;

解决方案 13:

以下所有工作在bashsh中都没有错误shellcheck,但您需要抑制SC2207

arrOrig=(&quot;192.168.3.4&quot; &quot;192.168.3.4&quot; &quot;192.168.3.3&quot;)

# NO SORTING
# shellcheck disable=SC2207
arr1=($(tr &#039; &#039; &#039;
&#039; &lt;&lt;&lt;&quot;${arrOrig[@]}&quot; | awk &#039;!u[$0]++&#039; | tr &#039;
&#039; &#039; &#039;)) # @estani
len1=${#arr1[@]}
echo &quot;${len1}&quot;
echo &quot;${arr1[*]}&quot;

# SORTING
# shellcheck disable=SC2207
arr2=($(printf &#039;%s
&#039; &quot;${arrOrig[@]}&quot; | sort -u)) # @das.cyklone
len2=${#arr2[@]}
echo &quot;${len2}&quot;
echo &quot;${arr2[*]}&quot;

# SORTING
# shellcheck disable=SC2207
arr3=($(echo &quot;${arrOrig[@]}&quot; | tr &#039; &#039; &#039;
&#039; | sort -u | tr &#039;
&#039; &#039; &#039;)) # @sampson-chen
len3=${#arr3[@]}
echo &quot;${len3}&quot;
echo &quot;${arr3[*]}&quot;

# SORTING
# shellcheck disable=SC2207
arr4=($(for i in &quot;${arrOrig[@]}&quot;; do echo &quot;${i}&quot;; done | sort -u)) # @corbyn42
len4=${#arr4[@]}
echo &quot;${len4}&quot;
echo &quot;${arr4[*]}&quot;

# NO SORTING
# shellcheck disable=SC2207
arr5=($(echo &quot;${arrOrig[@]}&quot; | tr &quot;[:space:]&quot; &#039;
&#039; | awk &#039;!a[$0]++&#039;)) # @faustus
len5=${#arr5[@]}
echo &quot;${len5}&quot;
echo &quot;${arr5[*]}&quot;

# 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, &#039;{a[$1];}END{for (i in a)print i;}&#039;

解决方案 16:

BASH 单行,不改变顺序,并且包含带空格的项目:

readarray -t my_array &lt; &lt;( (for i in &quot;${my_array[@]}&quot;; do echo &quot;$i&quot;; done) | awk &#039;!uniq[$0]++&#039; )

解决方案 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
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用