2010-08-26 38 views
12

Giả sử tôi có mảng bash (ví dụ mảng của tất cả tham số) và muốn xóa tất cả các tham số khớp với mẫu nhất định hoặc sao chép tất cả các phần tử còn lại sang mảng mới . Cách khác, làm tròn các yếu tố phù hợp với một mẫu.bash: cách xóa các phần tử khỏi mảng dựa trên mẫu

Một ví dụ để minh hoạ:

x=(preffoo bar foo prefbaz baz prefbar) 

và tôi muốn xóa tất cả mọi thứ bắt đầu với pref để có được

y=(bar foo baz) 

(thứ tự không liên quan)

gì nếu tôi muốn điều tương tự cho một danh sách các từ được phân tách bằng khoảng trắng?

x="preffoo bar foo prefbaz baz prefbar" 

và một lần nữa xóa mọi thứ bắt đầu với pref để có được

y="bar foo baz" 

Trả lời

5

Tước một chuỗi phẳng (Hu lk đã đưa ra câu trả lời cho mảng), bạn có thể bật tùy chọn extglob vỏ và chạy việc mở rộng sau

$ shopt -s extglob 
$ unset x 
$ x="preffoo bar foo prefbaz baz prefbar" 
$ echo ${x//pref*([^ ])?()} 
bar foo baz 

Tùy chọn extglob là cần thiết cho *(pattern-list)?(pattern-list) hình thức. Điều này cho phép bạn sử dụng cụm từ thông dụng (mặc dù ở dạng khác với các cụm từ thông dụng nhất) thay vì chỉ mở rộng tên đường dẫn (*?[).

Câu trả lời mà Hulk đã đưa ra cho các mảng sẽ chỉ hoạt động trên các mảng. Nếu nó xuất hiện để làm việc trên dây phẳng, nó chỉ vì trong thử nghiệm mảng không được bỏ đặt trước.

ví dụ:

$ x=(preffoo bar foo prefbaz baz prefbar) 
$ echo ${x[@]//pref*/} 
bar foo baz 
$ x="preffoo bar foo prefbaz baz prefbar" 
$ echo ${x[@]//pref*/} 
bar foo baz 
$ unset x 
$ x="preffoo bar foo prefbaz baz prefbar" 
$ echo ${x[@]//pref*/} 

$ 
+1

+1 cảm ơn vì đã xóa bỏ sự nhầm lẫn từ bài đăng của Hulk và chỉ ra con đường khác này. – kynan

4

Bạn có thể làm điều này:

Xóa tất cả lần xuất hiện của chuỗi con.

# Not specifing a replacement defaults to 'delete' ... 
echo ${x[@]//pref*/}  # one two three four ve ve 
#    ^^   # Applied to all elements of the array. 

Edit:

Đối với không gian trắng đó là loại giống

x="preffoo bar foo prefbaz baz prefbar" 
echo ${x[@]//pref*/} 

Output:

foo bar baz

+0

Mọi thứ tương tự với một chuỗi các từ được phân tách bằng khoảng trắng? – kynan

+0

Dường như điều đó không hoàn toàn hoạt động, điều đó sẽ xóa mọi thứ sau lần xuất hiện đầu tiên của 'pref' – kynan

+0

nhìn vào giải pháp của tôi. Hoặc tôi không hiểu câu hỏi của bạn – Hulk

8

Một cách khác để lột một chuỗi căn hộ là để chuyển nó sang một mảng sau đó sử dụng phương pháp mảng:

x="preffoo bar foo prefbaz baz prefbar" 
x=($x) 
x=${x[@]//pref*} 

Contrast này với điểm bắt đầu và kết thúc với một mảng:

x=(preffoo bar foo prefbaz baz prefbar) 
x=(${x[@]//pref*}) 
+0

Đã cho bạn một +1 để hiển thị cả hai và khiến tôi suy nghĩ ... –

+0

Tôi thực sự thích công cụ này vì nó thực sự làm giảm số lượng mã trước tôi sử dụng để làm cho loại hành động này. –

+0

Nó không thực sự hoạt động tốt với các mảng. Bắt một mảng ra khỏi đó là khó khăn nếu các yếu tố ban đầu chứa không gian ví dụ. Ví dụ như 'declare -a ARR = ('element1' 'với dấu cách' 'với hai dấu cách' 'element4')' và sau đó làm 'VAR = ($ {ARR [@] // element * /})'. Những gì bạn sẽ nhận được trong 'VAR' không phải là một mảng của hai phần tử (' với dấu cách' và 'có hai dấu cách') nhưng một mảng gồm năm phần tử (' with', 'space',' with', 'two', 'dấu cách'). –

1

Tôi đã xác định và sử dụng chức năng sau:

# Removes elements from an array based on a given regex pattern. 
# Usage: filter_arr pattern array 
# Usage: filter_arr pattern element1 element2 ... 
filter_arr() { 
    arr=([email protected]) 
    arr=(${arr[@]:1}) 
    dirs=($(for i in ${arr[@]} 
     do echo $i 
    done | grep -v $1)) 
    echo ${dirs[@]} 
} 

Ví dụ sử dụng:

$ arr=(chicken egg hen omelette) 
$ filter_arr "n$" ${arr[@]} 

Output:

trứng omelette

Kết quả của hàm là một chuỗi. Để chuyển đổi nó trở lại thành một mảng:

$ arr2=(`filter_arr "n$" ${arr[@]}`) 
+0

Nếu các phần tử mảng chứa các khoảng trống, điều này sẽ không bảo tồn chúng mà thay vào đó hãy chia mảng tạo thành phần tử mới. Bạn có thể thấy nó bằng cách có 'declare -a arr = ('element1' 'với dấu cách' 'với hai dấu cách' 'element4')' và lọc cho 'phần tử'. Kết quả thay vì chỉ chứa 'với dấu cách' và' có hai dấu cách' sẽ chứa mỗi từ là phần tử riêng biệt. –

3

Lọc một mảng rất khó nếu bạn xem xét khả năng chứa các phần tử (không kể các ký tự "weirder"). Trong các câu trả lời cụ thể cho đến nay (đề cập đến các hình thức khác nhau của ${x[@]//pref*/}) sẽ không thành công với các mảng như vậy.

Tôi đã điều tra vấn đề này một chút và tìm ra giải pháp tuy nhiên nó không phải là một lớp lót đẹp. Nhưng ít nhất nó là.

Ví dụ minh họa giả sử arr đặt tên mảng mà chúng tôi muốn lọc. Chúng ta sẽ bắt đầu với các biểu hiện cốt lõi:

for index in "${!ARR[@]}" ; do [[ …condition… ]] && unset -v 'ARR[$index]' ; done 
ARR=("${ARR[@]}") 

Hiện đã có vài yếu tố đáng nói:

  1. "${!ARR[@]}" để đánh giá chỉ số của mảng (như trái ngược với các yếu tố).
  2. Biểu mẫu "${!ARR[@]}" là điều bắt buộc. Bạn không được bỏ qua báo giá hoặc thay đổi @ thành *. Hoặc người nào khác biểu thức sẽ phá vỡ trên mảng kết hợp, nơi các phím có dấu cách (ví dụ).
  3. Phần sau do có thể là bất kỳ thứ gì bạn muốn. Ý tưởng chỉ là bạn phải làm unset như được hiển thị cho các phần tử mà bạn không muốn có trong mảng.
  4. It is advised or even needed để sử dụng -v và báo giá với unset hoặc những điều xấu khác có thể xảy ra.
  5. Nếu phần sau do như được đề xuất ở trên, bạn có thể sử dụng && hoặc || để lọc ra các yếu tố vượt qua hoặc thất bại điều kiện.
  6. Dòng thứ hai, chỉ định lại ARR, chỉ cần thiết với các mảng không liên kết và sẽ phá vỡ với mảng liên kết. (Tôi đã không nhanh chóng đưa ra một biểu thức chung mà sẽ xử lý cả trong khi tôi không cần một…). Đối với các mảng thông thường, nó là cần thiết nếu bạn muốn có các chỉ mục liên tiếp.Bởi vì unset trên một phần tử mảng không sửa đổi (giảm một) các phần tử của các chỉ mục cao hơn - nó chỉ làm cho một lỗ trong các chỉ mục. Bây giờ nếu bạn chỉ lặp qua mảng (hoặc mở rộng nó như một toàn thể) điều này làm cho không có vấn đề. Nhưng đối với các trường hợp khác, bạn cần gán lại các chỉ mục. Cũng lưu ý rằng nếu bạn có bất kỳ lỗ nào trong các chỉ mục trước khi nó cũng bị xóa. Vì vậy, nếu bạn cần bảo tồn các lỗ hiện có, cần phải thực hiện nhiều logic hơn bên cạnh số unset và giao lại lần cuối.

Hiện tại vì điều kiện này. Biểu thức [[ ]] là một cách dễ dàng nếu bạn có thể sử dụng nó. (Xem here.) Cụ thể là nó hỗ trợ đối sánh cụm từ thông dụng bằng cách sử dụng Extended Regular Expressions. (Xem here.) Cũng phải cẩn thận khi sử dụng grep hoặc bất kỳ công cụ dựa trên dòng nào khác cho điều này nếu bạn mong đợi rằng các phần tử mảng có thể chứa không chỉ các khoảng trống mà còn có các dòng mới. (Trong khi một tên tập tin rất khó chịu có thể có một ký tự dòng mới Tôi nghĩ ...)


Đề cập đến vấn đề bản thân khái niệm [[ ]] sẽ phải là:

[[ ${ARR[$index]} =~ ^pref ]] 

(với && unset như trên)


Cuối cùng hãy xem cách điều này hoạt động với những trường hợp khó khăn đó. Đầu tiên chúng ta xây dựng các mảng:

declare -a ARR='([0]="preffoo" [1]="bar" [2]="foo" [3]="prefbaz" [4]="baz" [5]="prefbar" [6]="pref with spaces")' 
ARR+=($'pref\nwith\nnew line') 
ARR+=($'\npref with new line before') 

chúng ta có thể thấy rằng chúng tôi có tất cả các trường hợp phức tạp bằng cách chạy declare -p ARR và nhận được:

declare -a ARR='([0]="preffoo" [1]="bar" [2]="foo" [3]="prefbaz" [4]="baz" [5]="prefbar" [6]="pref with spaces" [7]="pref 
with 
new line" [8]=" 
pref with new line before")' 

Bây giờ chúng ta chạy các biểu thức lọc:

for index in "${!ARR[@]}" ; do [[ ${ARR[$index]} =~ ^pref ]] && unset -v 'ARR[$index]' ; done 

và một thử nghiệm khác (declare -p ARR) cho dự kiến:

declare -a ARR='([1]="bar" [2]="foo" [4]="baz" [8]=" 
pref with new line before")' 

lưu ý cách tất cả các phần tử bắt đầu bằng pref đã bị xóa nhưng các chỉ mục không thay đổi. Cũng lưu ý rằng ${ARRAY[8]} vẫn còn ở đó vì nó bắt đầu bằng dòng mới thay vì pref.

Bây giờ cho các giao lại cuối cùng:

ARR=("${ARR[@]}") 

và kiểm tra (declare -p ARR):

declare -a ARR='([0]="bar" [1]="foo" [2]="baz" [3]=" 
pref with new line before")' 

đó là chính xác những gì được mong đợi.


Để ghi chú đóng. Nó sẽ được tốt đẹp nếu điều này có thể được thay đổi thành một lớp lót linh hoạt. Nhưng tôi không nghĩ rằng có một cách để làm cho nó ngắn hơn và đơn giản hơn như bây giờ mà không cần định nghĩa hàm hay giống nhau.

Đối với các chức năng nó sẽ được tốt đẹp cũng như có nó chấp nhận mảng, mảng trở lại và có dễ dàng để cấu hình thử nghiệm để loại trừ hoặc giữ. Nhưng tôi không đủ tốt với Bash để làm điều đó ngay bây giờ.

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