Пересечение массива в bash

Как вы сравниваете два массива в bash, чтобы найти все пересекающиеся значения?

Скажем:
array1 содержит значения 1 и 2
array2 содержит значения 2 и 3

В результате я должен вернуть 2.

Мой собственный ответ, который я еще не могу опубликовать из-за небольшой репутации:

for item1 in $array1; do
    for item2 in $array2; do
        if [[ $item1 = $item2 ]]; then
            result=$result" "$item1
        fi
    done
done

Я также ищу альтернативные решения.

Ответ 1

Элементы списка 1 используются как регулярное выражение, просмотренное в списке2 (выраженное как строка: ${list2 [*]}):

list1=( 1 2 3 4   6 7 8 9 10 11 12)
list2=( 1 2 3   5 6   8 9    11 )

l2=" ${list2[*]} "                    # add framing blanks
for item in ${list1[@]}; do
  if [[ $l2 =~ " $item " ]] ; then    # use $item as regexp
    result+=($item)
  fi
done
echo  ${result[@]}

Результат

1 2 3 6 8 9 11

Ответ 2

Принимая @Raihan ответ и работая с не файлами (хотя созданы FD) Я знаю, это немного обман, но, похоже, хорошая альтернатива.

Побочным эффектом является то, что выходной массив будет лексикографически отсортирован, надеюсь, что все в порядке (также не знаете, какой тип данных у вас есть, поэтому я просто тестировал с номерами, может потребоваться дополнительная работа, если у вас есть строки со специальными символами и т.д.)

result=($(comm -12 <(for X in "${array1[@]}"; do echo "${X}"; done|sort)  <(for X in "${array2[@]}"; do echo "${X}"; done|sort)))

Тестирование:

$ array1=(1 17 33 99 109)
$ array2=(1 2 17 31 98 109)

result=($(comm -12 <(for X in "${array1[@]}"; do echo "${X}"; done|sort)  <(for X in "${array2[@]}"; do echo "${X}"; done|sort)))

$ echo ${result[@]}
1 109 17

p.s. Я уверен, что был способ получить массив, чтобы вывести одно значение в строке без цикла for, я просто забыл (IFS?)

Ответ 3

Если это было два файла (вместо массивов), вы искали пересекающиеся строки, вы могли бы использовать команду comm.

$ comm -12 file1 file2

Ответ 4

Ваш ответ не будет работать по двум причинам:

  • $array1 просто расширяется до первого элемента array1. (По крайней мере, в моей установленной версии Bash, как это работает. Это не похоже на документальное поведение, поэтому это может быть зависящая от версии quirk.)
  • После добавления первого элемента в result, result будет содержать пробел, поэтому следующий запуск result=$result" "$item1 будет ужасно ошибочным. (Вместо добавления к result он выполнит команду, состоящую из первых двух элементов, при этом переменная среды result будет установлена ​​в пустую строку.) Исправление:. Оказывается, я был неверно об этом: разбиение слов не происходит внутри заданий. (См. Комментарии ниже.)

Что вы хотите, так это:

result=()
for item1 in "${array1[@]}"; do
    for item2 in "${array2[@]}"; do
        if [[ $item1 = $item2 ]]; then
            result+=("$item1")
        fi
    done
done

Ответ 5

Теперь, когда я понимаю, что вы подразумеваете под "массивом", я думаю, в первую очередь, что вы должны использовать фактические массивы Bash. Они гораздо более гибкие, в этом (например) элементы массива могут содержать пробелы, и вы можете избежать риска того, что * и ? будут инициировать расширение имени файла.

Но если вы предпочитаете использовать свой существующий подход в строках, разделенных пробелами, то я согласен с предложением RHT использовать Perl:

result=$(perl -e 'my %array2 = map +($_ => 1), split /\s+/, $ARGV[1];
                  print join " ", grep $array2{$_}, split /\s+/, $ARGV[0]
                 ' "$array1" "$array2")

(Разрывы строк просто для чтения, вы можете избавиться от них, если хотите.)

В приведенной выше команде Bash встроенная программа Perl создает хэш с именем %array2, содержащий элементы второго массива, а затем печатает любые элементы первого массива, которые существуют в %array2.

Это будет немного отличаться от вашего кода в том, как он обрабатывает повторяющиеся значения во втором массиве; в вашем коде, если array1 содержит x дважды, а array2 содержит x три раза, тогда result будет содержать x шесть раз, тогда как в моем коде result будет содержать только x дважды. Я не знаю, имеет ли это значение, поскольку я не знаю ваших точных требований.