Пересечение двух массивов в Bash

Можно наблюдать то, что процесс делает с командой strace. Strace показывает системные вызовы, выполненные процессом. Все ¹ процесс, который влияет на его среду, сделано через системные вызовы. Например, создание каталога может только быть сделано путем окончательного вызова mkdir системный вызов. mkdir команда оболочки является тонкой оберткой вокруг системного вызова того же имени.

Видеть что mkdir делает, выполненный

strace mkdir foo

Вы будете видеть много вызовов кроме mkdir (76 всего для успешного mkdir в моей системе), начиная с execve который загружает двухуровневое изображение процесса, затем звонит для загрузки библиотек и файлов данных, используемых программой, звонит для выделения памяти, звонит для наблюдения состояния системы, … Наконец вызовы команды mkdir и переносится вниз, заканчиваясь с exit_group.

Для наблюдения, что делает программа GUI запустите ее и только наблюдайте его во время одного действия. Узнайте идентификатор процесса программы (с ps x, htop или любое другое средство просмотра процесса), затем выполненный

strace -o file_manager.mkdir.strace -p1234

Это помещает трассировку от процесса 1234 в файле file_manager.mkdir.strace. Нажмите Ctrl+C для остановки strace не останавливая программу. Обратите внимание, что что-то как ввод имени каталога может включить тысячи или десятки тысяч системных вызовов: обработка движений мыши, изменения фокуса и так далее намного более сложны на том уровне, чем создание каталога.

Можно выбрать, какие системные вызовы зарегистрированы в strace произведенный путем передачи -e опция. Например, для исключения read, write и select:

strace -e \!read,write,select …

Только записывать mkdir вызовы:

strace -e mkdir …

¹ хорошо, почти все. Общая память только включает системный вызов начальной настройки.

12
12.12.2013, 10:13
5 ответов

comm(1) инструмент, который сравнивает два списка и может дать Вам пересечение или различие между двумя списками. Списки должны быть отсортированы, но этого легко достигнуть.

Получить Ваши массивы в отсортированный список, подходящий для comm:

$ printf '%s\n' "${A[@]}" | LC_ALL=C sort

Это повернет массив в отсортированный список. Сделайте то же для B.

Использовать comm возвратить пересечение:

$ comm -1 -2 file1 file2

-1 -2 говорит для удаления записей, уникальных для file1 (A) и уникальный для file2 (B) - пересечение двух.

Чтобы иметь его возвращают то, что находится в file2 (B), но не file1 (A):

$ comm -1 -3 file1 file2

-1 -3 говорит для удаления записей, уникальных для file1 и характерных для обоих - отъезд только уникальных для file2.

Подавать два конвейера в comm, используйте функцию "Process Substitution" bash:

$ comm -1 -2 <(pipeline1) <(pipeline2)

Получать это в массиве:

$ C=($(command))

Соединение всего этого:

# 1. Intersection
$ C=($(comm -12 <(printf '%s\n' "${A[@]}" | LC_ALL=C sort) <(printf '%s\n' "${B[@]}" | LC_ALL=C sort)))

# 2. B - A
$ D=($(comm -13 <(printf '%s\n' "${A[@]}" | LC_ALL=C sort) <(printf '%s\n' "${B[@]}" | LC_ALL=C sort)))
13
27.01.2020, 19:55
  • 1
    Это будет только работать, если Ваши значения не будут содержать \n. световой сигнал –  Chris Down 12.12.2013, 18:08
  • 2
    @ChrisDown:Верно. я всегда пытаюсь записать сценарии оболочки, которые правильно заключаются в кавычки и обрабатывают все символы, но я разочаровался в \n. Я никогда не видел его в имени файла, и большой набор работы инструментов Unix с \n разграничил записи, которые Вы теряете много, при попытке обработать \n как допустимый символ. –  camh 13.12.2013, 12:17
  • 3
    я видел его в именах файлов при использовании файловых менеджеров GUI, которые правильно не санируют входные имена файлов, которые копируются с где-то в другом месте (также, никто ничего не сказал об именах файлов). –  Chris Down 13.12.2013, 13:10
  • 4
    Защищать \n попробуйте это: arr1=( one two three "four five\nsix\nseven" ); arr2=( ${arr1[@]:1} "four five\\nsix" ); n1=${#arr1[@]}; n2=${#arr2[@]}; arr=( ${arr1[@]/ /'-_-'} ${arr2[@]/ /'-_-'} ); arr=( $( echo "${arr[@]}"|tr '\t' '-t-'|tr '\n' '-n-'|tr '\r' '-r-' ) ); arr1=( ${arr[@]:0:${n1}} ); arr2=( ${arr[@]:${n1}:${n2}} ); unset arr; printf "%0.s-" {1..10}; printf '\n'; printf '{'; printf " \"%s\" " "${arr1[@]}"; printf '}\n'; printf "%0.s-" {1..10}; printf '\n'; printf '{'; printf " \"%s\" " "${arr2[@]}"; printf '}\n'; printf "%0.s-" {1..10}; printf '\n\n'; unset arr1; unset arr2 –  Jason R. Mick 18.08.2015, 11:19

Можно получить все элементы, которые находятся и в A и в B цикличным выполнением через оба массива и сравнением:

A=(vol-175a3b54 vol-382c477b vol-8c027acf vol-93d6fed0 vol-71600106 vol-79f7970e vol-e3d6a894 vol-d9d6a8ae vol-8dbbc2fa vol-98c2bbef vol-ae7ed9e3 vol-5540e618 vol-9e3bbed3 vol-993bbed4 vol-a83bbee5 vol-ff52deb2)
B=(vol-175a3b54 vol-e38d0c94 vol-2a19386a vol-b846c5cf vol-98c2bbef vol-7320102b vol-8f6226cc vol-27991850 vol-71600106 vol-615e1222)

intersections=()

for item1 in "${A[@]}"; do
    for item2 in "${B[@]}"; do
        if [[ $item1 == "$item2" ]]; then
            intersections+=( "$item1" )
            break
        fi
    done
done

printf '%s\n' "${intersections[@]}"

Можно получить все элементы в B, но не в подобным образом:

A=(vol-175a3b54 vol-382c477b vol-8c027acf vol-93d6fed0 vol-71600106 vol-79f7970e vol-e3d6a894 vol-d9d6a8ae vol-8dbbc2fa vol-98c2bbef vol-ae7ed9e3 vol-5540e618 vol-9e3bbed3 vol-993bbed4 vol-a83bbee5 vol-ff52deb2)
B=(vol-175a3b54 vol-e38d0c94 vol-2a19386a vol-b846c5cf vol-98c2bbef vol-7320102b vol-8f6226cc vol-27991850 vol-71600106 vol-615e1222)

not_in_a=()

for item1 in "${B[@]}"; do
    for item2 in "${A[@]}"; do
        [[ $item1 == "$item2" ]] && continue 2
    done

    # If we reached here, nothing matched.
    not_in_a+=( "$item1" )
done

printf '%s\n' "${not_in_a[@]}"
4
27.01.2020, 19:55
  • 1
    Осуществление: если Вы чередуетесь A и B, intersections всегда то же до переупорядочения? –  Gilles 'SO- stop being evil' 13.12.2013, 00:39
  • 2
    @Gilles, Если массивы могут содержать дублирующиеся элементы, нет. –  Chris Down 14.12.2013, 07:04

При игнорировании эффективности вот подход:

declare -a intersect
declare -a b_only
for bvol in "${B[@]}"
do
    in_both=""
    for avol in "${A[@]}"
    do
        [ "$bvol" = "$avol" ] && in_both=Yes
    done
    if [ "$in_both" ]
    then
        intersect+=("$bvol")
    else
        b_only+=("$bvol")
    fi
done
echo "intersection=${intersect[*]}"
echo "In B only=${b_only[@]}"
1
27.01.2020, 19:55

Мой чистый удар путь

Как это переменные содержат только vol-XXX где XXX шестнадцатеричное число, существует быстрый способ использовать массивы удара

unset A B a b c i                    # Only usefull for re-testing...

A=(vol-175a3b54 vol-382c477b vol-8c027acf vol-93d6fed0 vol-71600106 vol-79f7970e
   vol-e3d6a894 vol-d9d6a8ae vol-8dbbc2fa vol-98c2bbef vol-ae7ed9e3 vol-5540e618
   vol-9e3bbed3 vol-993bbed4 vol-a83bbee5 vol-ff52deb2)
B=(vol-175a3b54 vol-e38d0c94 vol-2a19386a vol-b846c5cf vol-98c2bbef vol-7320102b
   vol-8f6226cc vol-27991850 vol-71600106 vol-615e1222)

for i in ${A[@]#vol-};do
    [ "${a[$((16#$i))]}" ] && echo Duplicate vol-$i in A
    ((a[$((16#$i))]++))
    ((c[$((16#$i))]++))
  done
for i in ${B[@]#vol-};do
    [ "${b[$((16#$i))]}" ] && echo Duplicate vol-$i in B
    ((b[$((16#$i))]++))
    [ "${c[$((16#$i))]}" ] && echo Present in A and B: vol-$i
    ((c[$((16#$i))]++))
  done

Это должно произвести:

Present in A and B vol-175a3b54
Present in A and B vol-98c2bbef
Present in A and B vol-71600106

В этом состоянии Вы колотите среду, содержите:

set | grep ^c=
c=([391789396]="2" [664344656]="1" [706295914]="1" [942425979]="1" [1430316568]="1"
[1633554978]="1" [1902117126]="2" [1931481131]="1" [2046269198]="1" [2348972751]="1"
[2377892602]="1" [2405574348]="1" [2480340688]="1" [2562898927]="2" [2570829524]="1"
[2654715603]="1" [2822487781]="1" [2927548899]="1" [3091645903]="1" [3654723758]="1"
[3817671828]="1" [3822495892]="1" [4283621042]="1")

Таким образом, Вы могли:

for i in ${!b[@]};do
    [ ${c[$i]} -eq 1 ] &&
        printf "Present only in B: vol-%8x\n" $i
  done

Это представит:

Present only in B: vol-27991850
Present only in B: vol-2a19386a
Present only in B: vol-615e1222
Present only in B: vol-7320102b
Present only in B: vol-8f6226cc
Present only in B: vol-b846c5cf
Present only in B: vol-e38d0c94

Но это численно отсортировано! Если Вы хотите первоначальный заказ, Вы могли:

for i in ${B[@]#vol-};do
    [ ${c[((16#$i))]} -eq 1 ] && printf "Present in B only: vol-%s\n" $i
  done

Так Вы dislay vols в том же порядке, как отправлено:

Present in B only: vol-e38d0c94
Present in B only: vol-2a19386a
Present in B only: vol-b846c5cf
Present in B only: vol-7320102b
Present in B only: vol-8f6226cc
Present in B only: vol-27991850
Present in B only: vol-615e1222

или

for i in ${!a[@]};do
    [ ${c[$i]} -eq 1 ] && printf "Present only in A: vol-%8x\n" $i
  done

для показа только в A:

Present only in A: vol-382c477b
Present only in A: vol-5540e618
Present only in A: vol-79f7970e
Present only in A: vol-8c027acf
Present only in A: vol-8dbbc2fa
Present only in A: vol-93d6fed0
Present only in A: vol-993bbed4
Present only in A: vol-9e3bbed3
Present only in A: vol-a83bbee5
Present only in A: vol-ae7ed9e3
Present only in A: vol-d9d6a8ae
Present only in A: vol-e3d6a894
Present only in A: vol-ff52deb2

или даже:

for i in ${!b[@]};do
    [ ${c[$i]} -eq 2 ] && printf "Present in both A and B: vol-%8x\n" $i
  done

переиздаст:

Present in both A and B: vol-175a3b54
Present in both A and B: vol-71600106
Present in both A and B: vol-98c2bbef
0
27.01.2020, 19:55
  • 1
    Конечно, если Duplicate строки бесполезны, они могли просто быть отброшены. –  F. Hauri 12.12.2013, 14:49

Для этого существует довольно элегантный и эффективный подход с использованием uniq - но нам нужно будет удалить дубликаты из каждого массива, оставляя только уникальные предметы. Если вы хотите сохранить дубликаты, есть только один способ «перебрать оба массива и сравнить».

Предположим, у нас есть два массива:

A=(vol-175a3b54 vol-382c477b vol-8c027acf vol-93d6fed0 vol-71600106 vol-79f7970e vol-e3d6a894 vol-d9d6a8ae vol-8dbbc2fa vol-98c2bbef vol-ae7ed9e3 vol-5540e618 vol-9e3bbed3 vol-993bbed4 vol-a83bbee5 vol-ff52deb2)
B=(vol-175a3b54 vol-e38d0c94 vol-2a19386a vol-b846c5cf vol-98c2bbef vol-7320102b vol-8f6226cc vol-27991850 vol-71600106 vol-615e1222)

Прежде всего, давайте преобразуем эти массивы в множества.Мы сделаем это, потому что существует математическая операция пересечения, которая известна как пересечение множеств, а множество - это набор различных объектов, различных или уникальных . Честно говоря, я не знаю, что такое «пересечение», если мы говорим о списках или последовательностях. Хотя мы можем выделить подпоследовательность из последовательности, но эта операция (выделение) имеет несколько иное значение.

Итак, трансформируемся!

$ A=(echo ${A[@]} | sed 's/ /\n/g' | sort | uniq)
$ B=(echo ${B[@]} | sed 's/ /\n/g' | sort | uniq)
  1. Пересечение:

     $ echo $ {A [@]} $ {B [@]} | sed 's / / \ n / g' | сортировать | uniq -d 
     

    Если вы хотите сохранить элементы в другом массиве:

     $ intersection_set = $ (echo $ {A [@]} $ {B [@]} | sed 's / / \ n / g '| sort | uniq -d) 
     
     $ echo $ crossction_set 
    vol-175a3b54 vol-71600106 vol-98c2bbef 
     

    uniq -d означает показывать только дубликаты (я думаю, uniq работает довольно быстро из-за своей реализации: я предполагаю, что это делается с помощью операции XOR ).

  2. Получить список элементов, которые появляются в B и недоступны в A , т. Е. B \ A

     $ echo $ {A [@]} $ {B [@]} | sed 's / / \ n / g' | сортировать | uniq -d | xargs echo $ {B [@]} | sed 's / / \ n / g' | сортировать | uniq -u 
     

    Или с сохранением в переменной:

     $ subtraction_set = $ (echo $ {A [@]} $ {B [@]} | sed 's / / \ n / g '| sort | uniq -d | xargs echo $ {B [@]} | sed' s / / \ n / g '| sort | uniq -u) 
     
     $ echo $ subtraction_set 
    vol-27991850 vol-2a19386a vol-615e1222 vol-7320102b vol-8f6226cc vol-b846c5cf vol-e38d0c94 
     

    Таким образом, сначала мы получили пересечение A и B (который представляет собой просто набор дубликатов между ними), скажем, это A / \ B , а затем мы использовали операцию инвертирования пересечения B и A / \ B (это просто уникальные элементы), поэтому мы получаем B \ A =! (B / \ (A / \ B)) .

P.S. uniq был написан Ричардом М. Столлманом и Дэвидом Маккензи.

3
27.01.2020, 19:55

Теги

Похожие вопросы