2009-02-18 34 views
9

Tôi đang gặp một số vấn đề với việc tách từ trong việc mở rộng biến bash. Tôi muốn có thể lưu trữ một danh sách đối số trong một biến và chạy nó, nhưng bất kỳ đối số nhiều từ được trích dẫn nào không đánh giá tôi mong đợi chúng như thế nào.Làm thế nào tôi có thể vượt qua một danh sách đối số đầy đủ trong bash trong khi vẫn giữ các đối số mulitword cùng nhau?

Tôi sẽ giải thích sự cố của mình bằng ví dụ. Cho phép nói rằng tôi đã có một chức năng decho rằng in mỗi tham số vị trí trên một dòng riêng của nó:

#!/bin/bash -u 
while [ $# -gt 0 ]; do 
    echo $1 
    shift 
done 

Ok, nếu tôi đi decho a b "c d" tôi nhận được:

[~]$ decho a b "c d" 
a 
b 
c d 

Đó là những gì tôi mong đợi và muốn. Nhưng mặt khác, nếu tôi nhận được danh sách các đối số từ một biến tôi nhận được điều này:

[~]$ args='a b "c d"' 
[~]$ decho $args 
a 
b 
"c 
d" 

Đó không phải là điều tôi muốn. Tôi có thể đi:

[~]$ echo decho $args | bash 
a 
b 
c d 

Nhưng điều đó có vẻ hơi khó chịu. Có cách nào tốt hơn để làm cho việc mở rộng $args trong số decho $args được phân tách theo cách tôi mong đợi không?

+0

FYI, 'echo $ 1' là khá đáng tiếc - nó thay thế, f'rinstance, một cuộc tranh cãi của' * 'với một danh sách các tên tập tin trong thư mục hiện hành. 'echo" $ 1 "' sẽ ít gây nguy hiểm hơn. –

Trả lời

4

Bạn có thể sử dụng:

eval decho $args 
0

Các bạn đã thử:

for arg in "[email protected]" 
do 
     echo "arg $i:$arg:" 
     let "i+=1" 
done 

nên mang lại một cái gì đó như:

arg 1: a 
arg 2: c d 

trong trường hợp của bạn.

Trực tiếp từ bộ nhớ, không có bảo đảm :-)

+0

Chức năng này thực hiện chính xác giống như decho. Vấn đề là gọi decho, không phải decho. –

0

hmmm .. eval decho $args làm việc quá:

[~]$ eval decho $args 
a 
b 
c d 

Và tôi có thể làm điều gì đó với mảng bash sử dụng "${array[@]}" (hoạt động như "[email protected]"), nhưng sau đó tôi sẽ phải viết mã để tải mảng, đó sẽ là một nỗi đau.

2

Bạn có thể di chuyển eval bên trong kịch bản:

#!/bin/bash -u 
eval set -- $* 
for i; 
do 
    echo $i; 
done 

Bây giờ bạn có thể làm:

$ args='a b "c d"' 
$ decho $args 
a 
b 
c d 

nhưng bạn sẽ phải trích lập luận nếu bạn vượt qua chúng trên CL:

$ decho 'a b "c d"' 
a 
b 
c d 
0

Về cơ bản là thiếu sót để cố chuyển một danh sách đối số được lưu trữ trong một biến, tới lệnh.

Có lẽ, nếu bạn có mã ở đâu đó để tạo biến chứa arg dự định.cho một lệnh, sau đó bạn có thể thay đổi nó để thay vào đó lưu trữ các args vào một biến mảng:

decho_argv=(a b 'c d') # <-- easy! 

Sau đó, chứ không phải là thay đổi lệnh "decho" để thích ứng với args lấy từ một biến đồng bằng (mà sẽ phá vỡ nó khả năng xử lý args bình thường), bạn có thể làm:

decho "${decho_argv[@]}" # USE DOUBLE QUOTES!!! 

Tuy nhiên, nếu bạn là tình huống mà bạn đang cố gắng để có đầu vào tùy ý mà dự kiến ​​sẽ là lĩnh vực chuỗi tương ứng với ý định tranh luận về vị trí chỉ huy, và bạn muốn chuyển các đối số đó cho một lệnh, sau đó bạn nên thay vì sử dụng một biến, đọc dữ liệu vào một mảng.

Lưu ý rằng các đề xuất cung cấp việc sử dụng eval để đặt thông số vị trí với nội dung của một biến thông thường là cực kỳ nguy hiểm.

Bởi vì, hiển thị nội dung của một biến để loại bỏ trích dẫn và tách từ trên dòng lệnh không có cách nào để bảo vệ chống lại vỏ metachars trong chuỗi trong biến gây havoc.

Ví dụ, hãy tưởng tượng trong ví dụ sau nếu từ "con người" được thay thế bằng hai chữ "rm" và "-rf" và arg quyết định cuối cùng là "*":

không làm điều này :

> args='arg1 ; man arg4' 
> eval set -- $args 
No manual entry for arg4 
> eval set -- "$args"  # This is no better 
No manual entry for arg4 
> eval "set -- $args"  # Still hopeless 
No manual entry for arg4 

> eval "set -- '$args'" # making it safe also makes it not work at all! 
> echo "$1" 
arg1 ; man arg4 
Các vấn đề liên quan