2011-10-24 36 views
11

Bạn so sánh hai mảng trong bash để tìm tất cả các giá trị giao nhau như thế nào?Giao điểm mảng trong bash

Hãy nói rằng:
array1 chứa giá trị 1 và 2
array2 chứa giá trị 2 và 3

tôi sẽ nhận lại 2 như một kết quả.

câu trả lời của tôi riêng, mà tôi không thể gửi được nêu ra do danh tiếng nhỏ:

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

Tôi đang tìm kiếm giải pháp thay thế là tốt.

+0

tôi don không nghĩ rằng bạn sẽ tìm thấy một cách tốt hơn để làm điều này. Bash không thực sự được xây dựng cho thao tác mảng, và tôi không thể nghĩ ra một công cụ dòng lệnh có thể được sử dụng để tìm giao điểm của hai mảng. –

+0

Đây là nơi Perl tỏa sáng. – RHT

Trả lời

12

Các yếu tố của danh sách 1 được sử dụng như biểu hiện thường xuyên nhìn lên trong List2 (thể hiện dưới dạng chuỗi: $ {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[@]} 

Kết quả là

1 2 3 6 8 9 11 
+0

Mặc dù có vẻ như nhiều câu trả lời được cung cấp cho câu hỏi này sẽ hoạt động đối với mảng hoặc danh sách giao lộ. Tôi đang chọn câu trả lời này vì nó không yêu cầu perl và dường như cung cấp một cắt ngắn không sử dụng một vòng lặp thứ hai thông qua regexp. Nó cũng trả lời câu hỏi ban đầu của giao điểm mảng, mặc dù tôi đang tìm kiếm các giao điểm danh sách, tôi nên viết lại các danh sách dưới dạng mảng. Cảm ơn mọi người. – dabest1

2

Nếu đó là hai tệp (thay vì mảng) bạn đang tìm kiếm các đường giao nhau, bạn có thể sử dụng lệnh comm.

$ comm -12 file1 file2 
+2

Điều này chỉ hoạt động nếu các tệp được sắp xếp. – ndn

1

câu trả lời của bạn sẽ không hoạt động, vì hai lý do:

  • $array1 chỉ mở rộng tới phần tử đầu tiên của array1. (Ít nhất, trong phiên bản Bash đã cài đặt của tôi đó là cách nó hoạt động. Điều đó dường như không phải là một hành vi được ghi lại tài liệu, do đó nó có thể là một dấu ngoặc đơn phụ thuộc vào phiên bản.)
  • Sau khi phần tử đầu tiên được thêm vào result, result sau đó sẽ chứa một khoảng trắng, do đó, lần chạy tiếp theo của result=$result" "$item1 sẽ hoạt động không tốt. (Thay vì gắn thêm result, nó sẽ chạy lệnh bao gồm hai mục đầu tiên, với biến môi trường result được đặt thành chuỗi rỗng.) Hiệu chỉnh: Biến ra, tôi đã sai về điều này: từ tách không diễn ra bên trong các bài tập. (Xem ý kiến ​​dưới đây.)

gì bạn muốn điều này là:

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

Có lẽ tôi đã nhận được mảng và danh sách bị nhầm lẫn. Có một sự khác biệt giữa mảng và danh sách trong bash? – dabest1

+1

@ dabest1: "Danh sách" không phải là thuật ngữ kỹ thuật trong Bash. Nếu bạn không có nghĩa là "mảng", thì tôi nghĩ bạn phải có cái gì đó mơ hồ, dọc theo dòng "một chuỗi chứa khoảng trống, nơi khoảng trắng nên được hiểu là tách các thành phần của chuỗi". Rõ ràng là không có thuật ngữ một từ cho điều đó. :-) Nếu bạn đăng một số mã xung quanh cho biết cách "các mảng" được khởi tạo và cách bạn sử dụng chúng, điều đó có thể sẽ làm rõ rất nhiều. – ruakh

+0

Ngoài ra - * bất kể * ý của bạn là gì, dòng của bạn 'result = $ result" "$ item1' sẽ không làm những gì bạn nghĩ, trừ khi bạn đã đặt biến' IFS' thành thứ gì đó lạ, mà tôi thực sự nghi ngờ bạn có. (Và nếu bạn * có * đặt biến 'IFS' thành cái gì đó lạ, thì bạn có vấn đề khác!) – ruakh

7

Lấy @ câu trả lời Raihan và làm cho nó làm việc với những người không-files (mặc dù FD được tạo ra) Tôi biết đó là một chút một cheat nhưng dường như tốt thay thế

Tác dụng phụ là mảng đầu ra sẽ được sắp xếp theo từ điển, hy vọng thats okay (cũng không kno loại dữ liệu bạn có, vì vậy tôi chỉ cần thử nghiệm với số, có thể có công việc bổ sung cần thiết nếu bạn có chuỗi ký tự đặc biệt s vv)

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

Thử nghiệm:

$ 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 

tái bút: Tôi chắc chắn có một cách để có được mảng để ra một giá trị cho mỗi dòng w/o vòng lặp for, tôi chỉ cần quên nó (IFS?)

+0

Giải pháp khá tốt - Im bối rối như những gì xảy ra với hai tập tin đầu vào std trong sub-shell - có vẻ như nó bằng cách nào đó bằng cách sử dụng/proc/self/fd, nhưng im không thể làm cho nó hoạt động với bất cứ thứ gì khác (ví dụ: cat/echo) – Soren

+0

@Soren: Xem http://www.gnu.org/s/bash/manual/bash.html#Process-Substitution. Mặc dù sự xuất hiện tương tự như chuyển hướng đầu vào std-đầu vào, những biểu thức thực sự được thay thế bằng tên tập tin. Tôi không biết tại sao bạn không thể làm cho nó hoạt động với 'con mèo '. Trên hệ thống của tôi, 'cat <(echo foo) <(echo bar)' in 'foo bar' (trên hai dòng). Điều đó không xảy ra với bạn? – ruakh

+3

'printf - '% s \ n'" $ {mảng [@]} "' sẽ xuất mỗi phần tử trên một dòng riêng biệt. –

0

Bây giờ tôi hiểu những gì bạn có nghĩa là "mảng", tôi suy nghĩ - trước hết - bạn nên xem xét sử dụng các mảng Bash thực tế. Chúng linh hoạt hơn nhiều, trong đó các phần tử mảng (ví dụ) có thể chứa khoảng trắng và bạn có thể tránh rủi ro *? sẽ kích hoạt mở rộng tên tệp.

Nhưng nếu bạn thích sử dụng cách tiếp cận hiện tại của bạn của chuỗi khoảng trắng được phân định, sau đó tôi đồng ý với đề nghị RHT để sử dụng Perl:

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

(Các dòng-chia chỉ là để có thể đọc, bạn có thể thoát khỏi Nếu bạn muốn.)

Trong lệnh Bash ở trên, chương trình Perl được nhúng tạo băm có tên %array2 chứa các phần tử của mảng thứ hai, sau đó in bất kỳ phần tử nào của mảng đầu tiên tồn tại trong %array2.

Điều này sẽ hoạt động hơi khác với mã của bạn trong cách xử lý các giá trị trùng lặp trong mảng thứ hai; trong mã của bạn, nếu array1 chứa x hai lần và array2 chứa x ba lần, sau đó result sẽ chứa x sáu lần, trong khi đó trong mã của tôi, result sẽ chứa x chỉ hai lần. Tôi không biết điều đó có quan trọng không, vì tôi không biết yêu cầu chính xác của bạn.

Các vấn đề liên quan