2011-10-24 22 views
11

如何比較bash中的兩個數組以找到所有相交值?bash中的數組交點

比方說:
陣列1包含值1和2
數組2中包含的值2和3

我應該找回2的結果。

我自己的答案,我還不能發佈,由於小有名氣:

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

我正在尋找替代解決方案也是如此。

+0

我不不認爲你會找到一個更好的方式來做到這一點。 Bash並不是真正爲數組操作而構建的,我無法想象一個可用於查找兩個數組相交的命令行工具。 –

+0

這是Perl發光的地方。 – RHT

回答

12

列表1所述的元件被用作正則表達式中list2中擡頭(表示爲字符串:$ {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 
+0

雖然看起來這個問題提供了很多答案可以用於數組或列表交集。我選擇這個答案,因爲它不需要Perl,似乎提供了通過正則表達式不使用第二個循環的捷徑。它也回答了數組交集的原始問題,儘管我正在尋找列表交集,我應該將列表重寫爲數組。感謝大家。 – dabest1

2

如果是兩個文件(而不是數組),您正在查找相交線,則可以使用comm命令。

$ comm -12 file1 file2 
+2

這隻適用於文件排序的情況。 – ndn

1

你的回答是行不通的,原因有二:

  • $array1剛剛展開爲array1的第一要素。 (至少,在我安裝的Bash版本中,它是如何工作的,這似乎不是一個記錄的行爲,因此它可能是一個版本依賴的怪癖。)
  • 在第一個元素被添加到result之後,result將包含一個空間,所以下一輪result=$result" "$item1將會糟糕透頂。 (而不是追加到result,它將運行由前兩項組成的命令,環境變量result被設置爲空字符串。)糾錯:原來,我錯了這個:word-splitting doesn在任務內部不會發生。 (見註釋下面。)

你想要的是這樣的:

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

也許我有數組和列表困惑。 bash中的數組和列表是否有區別? – dabest1

+1

@ dabest1:「列表」不是Bash中的技術術語。如果你的意思不是「數組」,那麼我認爲你的意思應該含糊不清,即「包含空格的字符串,其中空格應該被解釋爲分隔字符串的組成部分」。很顯然,這沒有一個單詞。 :-)如果你發佈了一些顯示這些「數組」如何初始化的代碼,以及你如何使用它們,這可能會澄清很多。 – ruakh

+0

另外 - *無論你的意思是什麼,你的行結果= $ result「」$ item1'不會做你的想法,除非你把IFS變量設置爲奇怪的東西,我真的懷疑你有。 (如果你把* IFS變量設置爲奇怪的話,那麼你就會遇到不同的問題!) – ruakh

7

以@ Raihan的答案,並使其與非文件的工作(雖然創建FDS) 我知道這一點一個作弊,但似乎是很好的替代

副作用是輸出數組將按字典順序排序,希望多數民衆贊成 (也不知道你有什麼類型的數據,所以我只是用數字測試,可能有如果你有特殊字符的字符串需要額外的工作小號等)

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 

附:我敢肯定,有一種方法可以讓數組每行出一個值,而不是for循環,我只是忘了它(IFS?)

+0

相當不錯的解決方案 - 我對在sub-shell中使用兩個std-input文件會發生什麼感到困惑 - 看起來它使用了/ proc/self/fd,但我無法讓它與其他任何東西一起工作(例如cat/echo) – Soren

+0

@Soren:請參閱http://www.gnu.org/s/bash/manual/bash.html#Process-Substitution。儘管外觀與標準輸入重定向相似,但這些表達式實際上被替換爲文件名。我不知道你爲什麼不能使用'cat'來工作。在我的系統上,'cat <(echo foo)<(echo bar)'打印'foo bar'(兩行)。這不會發生在你的身上嗎? – ruakh

+3

'printf - '%s \ n'「$ {array [@]}」'會在單獨的行上輸出每個元素。 –

0

現在,我明白你的意思是「數組」想想 - 首先 - 你應該考慮使用實際的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只有兩次。我不知道這是否重要,因爲我不知道你的具體要求。