【问题标题】:Get common elements in 3 arrays in shell scripting在shell脚本中获取3个数组中的公共元素
【发布时间】:2020-09-03 16:26:48
【问题描述】:

此代码适用于 2 个数组,但我想再添加一个数组进行比较,该怎么做? 谢谢

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[@]}

【问题讨论】:

  • 这能回答你的问题吗? join multiple files;或者:join &lt;(join &lt;(sort f1) &lt;(sort f2)) &lt;(sort f3);将结果输入目标数组应该很容易
  • 请注意,如果您的数组包含文字换行符,当前代码将表现不佳;使用readarray -d '' array1 &lt; &lt;(printf '%s\0' "${array1[@]}" | sort -nz) 可以避免这种情况。 (当然,如果都是数字也没关系)。
  • ...和一般,当您想要进行集合算术(查找两个排序流之间共同的值 - 或仅在一个或另一个中)时,适合这项工作的工具是comm,而不是尝试自己动手。见BashFAQ #36
  • (这里也有一些引用问题,像大拇指一样突出;当您不引用扩展时,您将失去"${array[@]}" 相对于${array[*]} 的所有优势)跨度>
  • ...要获取 comm 中三个已排序流之间的共同项目,您可以组合两个实例:comm -12 &lt;(printf '%s\n' "${sortedListOne[@]}") &lt;(comm -12 &lt;(printf '%s\n' "${sortedListTwo[@]}") &lt;(printf '%s\n' "${sortedListThree[@]}") ) -- 请注意 comm 需要字典排序,而不是数字排序。跨度>

标签: bash shell


【解决方案1】:

你的固定脚本:

#!/usr/bin/env bash

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

#sort both arrays
readarray -t array1 < <(printf "%s\n" "${array1[@]}" | sort -n)
readarray -t 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
  if [[ ${array1[$i1]} -eq ${array2[$i2]} ]]; then
    array3+=($((array1[i1])))
  fi
done

echo "${array3[@]}"

或者更短、更高效、更可靠的版本,可以处理任意数量的数组

#!/usr/bin/env bash

declare -ai array1=(20 30 40 50)
declare -ai array2=(10 20 30 80 100 110 40)
declare -ai array3=(30 50 80 40 10)
declare -ai array4=(110 80 30 50 40)
declare -i arrays_cnt=4
declare -ai common=()

# Or in one go
mapfile -d '' common < <(
  printf %d\\0 "${array1[@]}" "${array2[@]}" "${array3[@]}" "${array4[@]}" \
    | sort -zn \
    | uniq -zcd \
    | grep -z "^\ *$arrays_cnt" \
    | cut -zb9-
)

echo "${common[@]}"

如果真的不想使用外部工具,而是使用 Bash 4.2+ 关联数组:

#!/usr/bin/env bash

declare -ai array1=(20 30 40 50)
declare -ai array2=(10 20 30 80 100 110 40)
declare -ai array3=(30 50 80 40 10)
declare -ai array4=(110 80 30 50 40)
declare -i arrays_cnt=4
declare -ai common=()
declare -Ai key_count=()

# Count occurrences of keys
for k in "${array1[@]}" "${array2[@]}" "${array3[@]}" "${array4[@]}"; do
  key_count[$k]+=1
done

# Populates common array with non-unique keys
for k in "${!key_count[@]}"; do
  [ ${key_count[$k]} -eq $arrays_cnt ] && common+=("$k")
done

echo "${common[@]}"

【讨论】:

  • 谢谢 Léa,如果我再添加一个数组?
  • @user14216283 更多的数组是完全一样的和简单的。只需将额外的数组添加到print %d\\0
  • 我添加了一个新数组,但结果不是3个数组中唯一的公共值,结果是出现多次的值。
  • @user14216283 增加了对任意数量数组的支持
猜你喜欢
  • 2013-06-06
  • 1970-01-01
  • 2012-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-17
  • 2018-08-07
相关资源
最近更新 更多