【问题标题】:Using "comm" to find matches between two arrays使用“comm”查找两个数组之间的匹配项
【发布时间】:2019-09-25 14:04:09
【问题描述】:

我有两个数组,我正在尝试使用 comm 查找匹配值。 Array1 在每个元素中都包含一些附加信息,我将这些信息剥离出来进行比较。但是,我想在比较完成后保留该信息。

例如:

Array1=("abc",123,"hello" "def",456,"world")
Array2=("abc")
declare -a Array1
declare -a Array2

然后我比较两个数组:

oldIFS=$IFS IFS=$'\n\t'
array3=($(comm -12 <(echo "${Array1[*]}" | awk -F "," {'print $1'} | sort) <(echo "${Array2[*]}" | sort)))
IFS=$oldIFS

找到abc的匹配项:

echo ${test3[0]}
abc

但是我想要的是 array1 中不属于我的comm 语句的剩余值。

abc,123,hello

编辑:更多说明

本示例中的数组填充了虚拟数据。

我的真实示例是从我保存到 array1 的服务器日志中提取信息。 array1 包含 (userIDs,hostIPs,count),我想针对用户 ID (array2) 的列表进行交叉引用。我的目标是找出 array1 和 array2 中存在哪些用户 ID,并将这些 ID 与 array1 (hostIPs,count) 中的附加信息一起保存到 array3 中

array1 由一个变量填充,该变量是生成 splunk 搜索的 curl 命令的结果。返回的数据如下所示:

"uniqueID=<ID>","<IP>","<hostname>",1

我将 splunk 报告的结果保存为 $splunk,然后使用 $splunk 的结果对 array1 进行 decalare - 因为结果以 csv 格式返回后的标头信息

array1=( $(echo $splunk | sed 's/ /\n/g' | sed 1d) )

array2 是从我在本地存储的主文件生成的。它包含我们生态系统中的所有应用程序 ID。例如

uid=<ID>

我把主文件的内容放到array2中

array2=( $(cat master.txt) )

然后我想从 array1 中找出哪些 ID 存在于 array2 中,并将其保存为 array3。这需要对 array1 中的数据进行一些按摩,以使其与 array2 的格式相匹配。

oldIFS=$IFS IFS=$'\n\t'
array3=($(comm -12 <(echo "${array1[*]}" | sed 's/ /\n/g' | awk -F "\"," {'print $1'} | sed 's/\"//g' | sed 's/|/ /g' | awk -F$'=' -v OFS=$'=' '{ $1 = "uid" }1' | grep -i "OU=People" | sed 's/OU/ou/g' | sort) <(echo "${array2[*]}" | sort)))
IFS=$oldIFS

数组 3 将包含两个数组中匹配的行

uid=<ID>
uid=<ID>

但是我正在寻找更多的东西

"uid=<ID>","<IP>","<hostname>",1
"uid=<ID>","<IP>","<hostname>",1

【问题讨论】:

  • 为什么不用"def",456,"world" 呢?还有什么阻止你使用Array1
  • 已编辑问题以获得更多说明
  • 你如何定义这些数组?
  • 我的意思是,你究竟是如何分配给虚拟数组的?不清楚您是否假设元素是逗号或空格分隔的。
  • 对不起,如果我不清楚。我的意思是你应该编辑这个问题,以便我可以在这里测试。而不是Array1{ "abc",123,"hello" "def",456,"world" } 之类的Array1=(abc,123,hello def,456,world)(或者应该是(abc 123 hello def 456 world)?)。使用您的符号,我不知道数组中 真正 是什么。

标签: arrays bash shell


【解决方案1】:

我会这样做:

join -t, \
    <(printf '%s\n' "${Array1[@]}" | sort -t, -k1,1) \
    <(printf '%s\n' "${Array2[@]}" | sort)

使用join 命令和, 作为字段分隔符。第一个“文件”是第一个数组,每行一个元素,按第一个字段排序(逗号分隔);第二个“文件”是第二个数组,每行一个元素,已排序。

输出将是第一个文件的第一个元素与第二个文件的元素匹配的每一行;对于示例输入,它是

abc,123,hello

这仅做了一个假设,即没有数组元素包含换行符。为了使它更健壮(假设 GNU Coreutils),我们可以使用 NUL 作为分隔符:

join -z -t, \
    <(printf '%s\0' "${Array1[@]}" | sort -z -t, -k1,1) \
    <(printf '%s\0' "${Array2[@]}" | sort -z)

这也会打印由 NUL 分隔的输出;要将结果读入数组,我们可以使用readarray:

readarray -d '' -t Array3 < <(
    join -z -t, \
        <(printf '%s\0' "${Array1[@]}" | sort -z -t, -k1,1) \
        <(printf '%s\0' "${Array2[@]}" | sort -z)
)

readarray -d 需要 Bash 4.4 或更高版本。对于较旧的 Bash,您可以使用循环:

while IFS= read -r -d '' element; do
    Array3+=("$element")
done < <(
    join -z -t, \
        <(printf '%s\0' "${Array1[@]}" | sort -z -t, -k1,1) \
        <(printf '%s\0' "${Array2[@]}" | sort -z)
)

【讨论】:

    【解决方案2】:

    我不知道如何使用comm 执行此操作,但我确实为您提供了sedgrep 的解决方案。以下命令匹配正则表达式uid=X,,其中字符串/数组分别采用uid=x(uid=x uid=y) 的形式。

    # Array 2 (B) is a string
    $ A=("uid=1,10.10.10.1,server1,1" "uid=2,10.10.10.2,server2,1")
    $ B="uid=1"
    $ echo ${A[@]} | grep -oE "([^ ]*${B},[^ ]*)"
    uid=1,10.10.10.1,server1,1
    
    # Array 2 (D) is an array
    $ C=(${A[@]} "uid=3,10.10.10.3,server3,1" "uid=4,10.10.10.4,server4,1")
    $ D=(${B} "uid=3")
    $ echo ${C[*]} | grep -oE "([^ ]*($(echo ${D[@]} | sed 's/ /,|/g'))[^ ]*)"
    uid=1,10.10.10.1,server1,1
    uid=3,10.10.10.3,server3,1
    
    # Content of arrays
    $ echo ${A[@]}
    uid=1,10.10.10.1,server1,1 uid=2,10.10.10.2,server2,1
    $ echo ${B}
    uid=1
    $ echo ${C[@]}
    uid=1,10.10.10.1,server1,1 uid=2,10.10.10.2,server2,1 uid=3,10.10.10.3,server3,1 uid=4,10.10.10.4,server4,1
    $ echo ${D[@]}
    uid=1 uid=3
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-03
      • 2012-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-06
      • 1970-01-01
      • 2018-10-12
      相关资源
      最近更新 更多