2012-02-03 32 views
18

Trong bash, nếu bạn làm điều này:Có một cách dễ dàng để thiết lập nullglob cho một glob

mkdir /tmp/empty 
array=(/tmp/empty/*) 

bạn thấy rằng array hiện nay có một phần tử, "/tmp/empty/*", không không như bạn muốn. Rất may, điều này có thể tránh được bằng cách bật tùy chọn nullglob vỏ bằng shopt -s nullglob

Nhưng nullglob là toàn cầu, và khi chỉnh sửa một shell script hiện có, có thể phá vỡ mọi thứ (ví dụ, đã có người kiểm tra mã lối ra của ls foo* để kiểm tra xem có là các tệp có tên bắt đầu bằng "foo"?). Vì vậy, lý tưởng, tôi muốn bật nó chỉ cho một phạm vi nhỏ - lý tưởng, một tên tập tin mở rộng. Bạn có thể tắt nó đi một lần nữa sử dụng shopt -u nullglob Nhưng tất nhiên chỉ khi nó đã được vô hiệu hóa trước:

old_nullglob=$(shopt -p | grep 'nullglob$') 
shopt -s nullglob 
array=(/tmp/empty/*) 
eval "$old_nullglob" 
unset -v old_nullglob 

làm cho tôi nghĩ rằng phải có một cách tốt hơn. Rõ ràng "đặt nó trong một subshell" không hoạt động như tất nhiên chuyển nhượng biến chết với subshell. Ngoài waiting for the Austin group để nhập cú pháp ksh93, phải không?

Trả lời

9

Với mapfile trong Bash 4, bạn có thể tải một mảng từ một subshell với một cái gì đó như : mapfile array < <(shopt -s nullglob; for f in ./*; do echo "$f"; done). Full dụ:

$ shopt nullglob 
nullglob  off 
$ find 
. 
./bar baz 
./qux quux 
$ mapfile array < <(shopt -s nullglob; for f in ./*; do echo "$f"; done) 
$ shopt nullglob 
nullglob  off 
$ echo ${#array[@]} 
2 
$ echo ${array[0]} 
bar baz 
$ echo ${array[1]} 
qux quux 
$ rm * 
$ mapfile array < <(shopt -s nullglob; for f in ./*; do echo "$f"; done) 
$ echo ${#array[@]} 
0 
  • Hãy chắc chắn để glob với ./* thay vì một trần * khi sử dụng echo để in tên tập tin
  • Không làm việc với các nhân vật newline trong filename :(như chỉ ra bởi derobert

Nếu bạn cần để xử lý dòng mới trong tên tập tin, bạn w bị bệnh phải làm được nhiều chi tiết hơn:

array=() 
while read -r -d $'\0'; do 
    array+=("$REPLY") 
done < <(shopt -s nullglob; for f in ./*; do printf "$f\0"; done) 

Nhưng theo thời điểm này, có thể đơn giản hơn là làm theo lời khuyên của một trong những câu trả lời khác.

+1

Điều này khá tốt. Nó sẽ thất bại nếu tên tập tin có dòng mới trong chúng, nhưng mặc dù được cho phép, nó khá hiếm. Miễn là tên tập tin không nằm dưới sự kiểm soát của kẻ tấn công. – derobert

+0

Có, dòng mới trong tên tệp tồn tại. Thông thường, điều đó chỉ có nghĩa là "tìm ra thủ phạm và trừng phạt người đó". Ngoại trừ rằng thời gian duy nhất tôi nhìn thấy dòng mới trong tên tập tin, thủ phạm là một phụ nữ lớn tuổi quyến rũ ... – mivk

1

Điều này có thể gần với những gì bạn muốn; như vậy, nó yêu cầu thực hiện một lệnh để mở rộng glob.

$ ls 
file1 file2 
$ array=($(shopt -s nullglob; ls foo*)) 
$ ls foo* 
ls: foo*: No such file or directory 
$ echo ${array[*]} 
file1 file2 

Thay vì đặt array trong subshell, chúng tôi tạo ra một subshell sử dụng $() mà đầu ra được chụp bởi array.

+5

Không hoạt động với không gian (không có gì ngạc nhiên): 'chạm vào" tệp1 "" tệp 2 "; mảng = ($ (shopt -s nullglob; ls foo *)); thiết lập | grep^mảng' tạo 'mảng = ([0] =" tệp1 "[1] =" tệp "[2] =" 2 ")' – derobert

13

Bỏ đặt nó khi thực hiện:

shopt -u nullglob 

Và đúng (tức là lưu trữ các trạng thái trước đó):

shopt -u | grep -q nullglob && changed=true && shopt -s nullglob 
... do whatever you want ... 
[ $changed ] && shopt -u nullglob; unset changed 
+1

Điều đó tránh được eval, và tôi không nhận ra -s và -u đã có hành vi đó ('help shopt' không đề cập đến nó). Tôi đang upvoting nó cho những người. Nhưng thành thật mà nói, nó không thực sự đơn giản - về những gì bạn phải nhớ để có được quyền, có rất nhiều trong dòng đầu tiên đó. – derobert

+1

Tôi không bao giờ nói nó đơn giản hơn :-) Nhưng nó chỉ làm những gì câu hỏi của bạn hỏi, sử dụng nullglob mà không ảnh hưởng đến các tập lệnh khác bắt đầu từ cùng một phiên. Việc nhớ giá trị của nullglob thêm độ phức tạp, nhưng đó là cách duy nhất bạn có thể _preserve_ môi trường như nó đã được. – estani

+0

Vâng, câu hỏi là cho một cách * tốt hơn * hoặc * dễ dàng hơn * để làm điều đó (như ví dụ mã thứ hai trong câu hỏi cũng quản lý để khôi phục cài đặt về trạng thái trước đó của nó). Rõ ràng, tôi đã không làm rõ điều đó trong câu hỏi - hãy đề nghị cách cải thiện nó. – derobert

6

Đây chỉ là một chút tốt hơn đề xuất ban đầu của bạn:

local nullglob = $ (shopt -p nullglob); shopt -s nullglob

... làm bất cứ điều gì bạn muốn ...

$ nullglob; unset nullglob

0

Đây là giải pháp đơn giản nhất tôi đã tìm thấy:

Ví dụ, để mở rộng nghĩa đen **/*.mp3 vào một glob cho chỉ một biến cụ thể, bạn có thể sử dụng

VAR=**/*.mp3(N) 

Nguồn: https://unix.stackexchange.com/a/204944/56160

+1

Lưu ý rằng là một tính năng zsh không tìm thấy trong bash, mà OP quy định. – ssmith

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