【问题标题】:Get common values in 2 arrays in shell scripting [duplicate]在shell脚本中获取2个数组中的公共值[重复]
【发布时间】:2013-06-06 15:42:46
【问题描述】:

我有一个

array1 = (20,30,40,50)
array2 = (10,20,30,80,100,110,40)

我必须从我的数组 3 中的这 2 个数组中获取公共值,例如:

array3 = (20,30,40) 

按升序排列。

【问题讨论】:

  • 为什么要在 shell 中这样做? shell 只是一个调用工具的环境,它不是通用的编程语言,尽管它有一些编程语言结构来帮助你对任务进行排序。
  • 我想你要找的词是intersection
  • 对于纯 bash 解决方案,请参阅 stackoverflow.com/a/7877514/297323
  • 什么shell使用逗号分隔数组?
  • 奥斯卡颁奖典礼...? ;)

标签: bash shell unix sorting awk


【解决方案1】:

考虑使用python:

In [6]: array1 = (20,30,40,50)

In [7]: array2 = (10,20,30,80,100,110,40)

In [8]: set(array1) & set(array2)
Out[8]: set([40, 20, 30])

【讨论】:

  • 这个问题清楚地询问了关于 shell 脚本的问题。
【解决方案2】:

Shell 和标准 Unix 实用程序擅长处理文本文件。

在那个领域,数组是文本文件,其元素是行。

要找到两个这样的数组之间的公共部分,有标准的comm 命令。 comm 需要按字母排序的输入。

因此,如果您有两个文件 AB 包含这两个数组的元素,每行一个(这也意味着数组元素不能包含换行符),您可以找到与

comm -12 <(sort A) <(sort B)

如果您想从 bash 数组开始(但在 shell 中使用数组通常表明您使用了错误的工具来完成任务),您可以在 bash 数组和我们的文本文件行数组,带有printf '%s\n' 和分词:

array_one=(20 30 40 50)
array_two=(10 20 30 80 100 110 40)
IFS=$'\n'; set -f
intersection=($(comm -12 <(
   printf '%s\n' "${array_one[@]}" | sort) <(
   printf '%s\n' "${array_two[@]}" | sort)))

【讨论】:

  • +1。裸sort输出词汇顺序:如果OP想要数字顺序,comm输出需要通过sort -n
【解决方案3】:

使用数组的纯 解决方案:

#!/bin/bash

array1=(20,30,40,50)
array2=(10,20,30,80,100,110,40)

IFS=,
for i in $array1 $array2;{ ((++tmp[i]));}
for i in ${!tmp[*]};{ [ ${tmp[i]} -gt 1 ] && array3+=($i);}
echo ${array3[*]}

输出

20 30 40

由于array3 不是关联数组,因此索引使用${!array[*]} 表示法以升序排列。如果您需要逗号分隔的列表作为输入,请使用echo "${array3[*]}"

如果源元素是整数,则可以使用它。只有当每个源数组都包含唯一的数字时,它才有效..

【讨论】:

  • 为了消除混乱,请注意array1=(20,30,40,50),即使是IFS=,,也将一个元素分配给array1。与array1[0]='20,30,40,50' 相同。然而,由于IFS=, 并且因为${array1[*]} 稍后没有被引用,所以一个元素在那时被拆分为2030...
  • @sch:正确!感谢您的评论!代码已修改!
【解决方案4】:

除了这些好的答案之外,您似乎还想按升序对数组(包含答案)进行排序。

您可以通过多种不同的方式做到这一点,包括:

readarray array3 <<<"$(printf "%s\n" "${array3[@]}" | sort -n)"

此方法还允许您过滤掉重复值:

readarray array3 <<<"$(printf "%s\n" "${array3[@]}" | sort -n | uniq)"

为了练习,这里还有另一种解决方法:

#!/bin/bash

array1=(20 30 40 50)
array2=(10 20 30 80 100 110 40)
declare -a array3

#sort both arrays
readarray array1 <<<"$(printf "%s\n" "${array1[@]}" | sort -n)"
readarray array2 <<<"$(printf "%s\n" "${array2[@]}" | sort -n)"

# look for values
i2=0
for i1 in ${!array1[@]}; do
    while (( i2 < ${#array2[@]} && ${array1[$i1]} > ${array2[$i2]} )); do (( i2++ )); done
    [[ ${array1[$i1]} == ${array2[$i2]} ]] && array3+=(${array1[$i1]})
done


echo ${array3[@]}

【讨论】:

  • 而不是echo ${array1[@]} | tr ' ' '\n',使用printf "%s\n" "${array1[@]}"——如果任何数组元素包含空格,回显管道将给出错误的结果。
  • 已编辑,谢谢。解决方案是专门针对数字。
【解决方案5】:

您几乎可以肯定不应该为此使用 shell,所以这里有一个针对您的特定问题的 awk 解决方案:

awk 'BEGIN{
    split("20,30,40,50",array1,/,/)
    split("10,20,30,80,100,110,40",array2,/,/)

    for (i=1;i in array1;i++)
        for (j=1;j in array2;j++)
            if (array1[i] == array2[j])
                array3[++k] = array1[i]

    for (k=1; k in array3; k++)
        printf "array3[%d] = %d\n",k,array3[k]
}'
array3[1] = 20
array3[2] = 30
array3[3] = 40

如果您告诉我们您真正想要做什么,您可以获得更多帮助。

【讨论】:

    【解决方案6】:

    这是使用标准命令行工具(sortjoin)的解决方案:

    join <(printf %s\\n "${array1[@]}" | sort -u) \
         <(printf %s\\n "${array2[@]}" | sort -u) | sort -n
    

    join 要求对其输入进行排序,并且不识别数字排序顺序。因此,我以默认排序顺序对两个列表进行排序,将它们连接起来,然后以数字方式对结果进行排序。

    我还假设您将数组创建为真正的数组,即:

    array1=(20 30 40 50)
    

    我认为其余的或多或少是不言而喻的,可能在help printfman bash的帮助下。

    【讨论】:

      【解决方案7】:

      也许你可以使用 perl 来试试。

      #!/bin/perl
      use warnings;
      use strict;
      
      my @array1 = (20,30,40,50);
      my @array2 = (10,20,30,80,100,110,40);
      my @array3 = ();
      foreach my $x (@array1) {
          # body...
          if (grep(/$x/, @array2)){
              print "found $x\n";
              @array3=(@array3,$x);
          };
      }
      print @array3
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-22
        • 1970-01-01
        • 1970-01-01
        • 2012-05-25
        • 1970-01-01
        • 2018-03-06
        • 2013-11-09
        • 2021-08-25
        相关资源
        最近更新 更多