2008-09-19 41 views

Trả lời

96

Trích dẫn khi thiết $ FOO là không đủ. Bạn cần phải trích dẫn tài liệu tham khảo biến cũng như:

me$ FOO="BAR * BAR" 
me$ echo "$FOO" 
BAR * BAR 
+0

điều này là bí ẩn, tại sao điều này? những gì đang xảy ra? – tofutim

3
FOO='BAR * BAR' 
echo "$FOO" 
+0

này hoạt động, nhưng nó không phải là cần thiết để thay đổi dấu nháy đơn trên dòng đầu tiên để tăng gấp đôi dấu ngoặc kép. – finnw

+1

Không, không phải vậy, nhưng thói quen sử dụng dấu nháy đơn là thích hợp hơn khi bạn sẽ bao gồm các ký tự shell đặc biệt và bạn không muốn bất kỳ sự thay thế nào. – tzot

3
echo "$FOO" 
3

Nó có thể là giá trị nhận được vào các thói quen của việc sử dụng printf thay vì sau đó echo trên dòng lệnh.

Trong ví dụ này, nó không mang lại nhiều lợi ích nhưng nó có thể hữu ích hơn với đầu ra phức tạp hơn.

FOO="BAR * BAR" 
printf %s "$FOO" 
+0

Tại sao lại ở địa ngục? printf là một quá trình riêng biệt (ít nhất, không phải là một built-in bash) và việc sử dụng printf mà bạn chứng minh không có lợi ích hơn echo. – ddaa

+1

printf * là * một bash được xây dựng trong và tôi đã nói trong câu trả lời của tôi "Trong ví dụ này nó không mang lại nhiều lợi ích nhưng nó có thể hữu ích hơn với đầu ra phức tạp hơn. –

73

NGẮN ĐÁP

Giống như những người khác đã nói - bạn nên luôn luôn trích dẫn các biến để ngăn chặn hành vi kỳ lạ. Vì vậy, hãy sử dụng tiếng vang "$ foo" thay vì chỉ echo $ foo.

DÀI ĐÁP

Tôi nghĩ rằng ví dụ ích này là do giải thích thêm vì có nhiều diễn ra bên ngoài nó có vẻ trên khuôn mặt của nó.

tôi có thể nhìn thấy nơi sự nhầm lẫn của mình do thỏa thuận hợp vì sau khi bạn chạy ví dụ đầu tiên của bạn, bạn có thể nghĩ đến bản thân mà vỏ được rõ ràng thực hiện:

  1. mở rộng Parameter
  2. mở rộng ảnh Tên

Vì vậy, từ ví dụ đầu tiên của bạn:

me$ FOO="BAR * BAR" 
me$ echo $FOO 

A fter mở rộng tham số tương đương với:

me$ echo BAR * BAR 

Và sau khi mở rộng tên tập tin là tương đương với:

me$ echo BAR file1 file2 file3 file4 BAR 

Và nếu bạn chỉ cần gõ echo BAR * BAR vào dòng lệnh, bạn sẽ thấy rằng họ là tương đương.

Vì vậy, bạn có thể nghĩ đến bản thân "nếu tôi thoát khỏi *, tôi có thể ngăn chặn việc mở rộng tên tập tin"

Vì vậy, từ ví dụ thứ hai của bạn:

me$ FOO="BAR \* BAR" 
me$ echo $FOO 

Sau khi mở rộng tham số phải tương đương với:

me$ echo BAR \* BAR 

Và sau khi mở rộng tên tập tin nên được tương đương với:

me$ echo BAR \* BAR 

Và nếu bạn thử gõ "echo BAR \ * BAR" trực tiếp vào dòng lệnh, nó thực sự sẽ in "BAR * BAR" vì việc mở rộng tên tệp được ngăn chặn bằng cách thoát.

Vậy tại sao việc sử dụng $ foo không hoạt động?

Đó là vì có bản mở rộng thứ ba diễn ra - Trích dẫn xóa. Từ bash loại bỏ bằng tay quote là:

Sau khi mở rộng trước, tất cả lần xuất hiện không thể viện chứng của các nhân vật '\', ''', và '"' mà không gây từ một trong những bản mở rộng trên là gỡ bỏ.

vì vậy, những gì xảy ra là khi bạn gõ lệnh trực tiếp vào dòng lệnh, các ký tự thoát không phải là kết quả của một sự mở rộng trước đó để BASH loại bỏ nó trước khi gửi nó vào lệnh echo, nhưng trong ví dụ thứ 2, "\ *" là kết quả của việc mở rộng Tham số trước đó, vì vậy nó KHÔNG bị loại bỏ Kết quả là, echo nhận được "\ *" và đó là những gì nó in.

Lưu ý sự khác biệt giữa ví dụ đầu tiên - "*" không được bao gồm trong các ký tự sẽ bị xóa bởi Trích dẫn xóa.

Tôi hy vọng điều này có ý nghĩa. Cuối cùng kết luận trong cùng - chỉ cần sử dụng dấu ngoặc kép. Tôi chỉ nghĩ rằng tôi sẽ giải thích lý do tại sao thoát, mà hợp lý nên làm việc nếu chỉ có Parameter và mở rộng tên tập tin đang chơi, không hoạt động.

Đối với một giải thích đầy đủ các hoạt động mở BASH, hãy tham khảo:

http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions

+0

Câu trả lời hay! Bây giờ tôi không cảm thấy tôi hỏi một câu hỏi ngu ngốc :-) – andyuk

+1

Có tồn tại một số tiện ích để thoát khỏi các ký tự đặc biệt? –

+0

"bạn nên luôn luôn trích dẫn các biến để ngăn chặn hành vi lạ" - khi bạn muốn sử dụng chúng như chuỗi – Angelo

37

Tôi sẽ thêm một chút vào chủ đề cũ này.

Thông thường, bạn sẽ sử dụng

$ echo "$FOO" 

vấn đề Tuy nhiên, tôi đã có ngay cả với cú pháp này. Hãy xem xét kịch bản sau đây.

#!/bin/bash 
curl_opts="-s --noproxy * -O" 
curl $curl_opts "$1" 

Các * nhu cầu để được thông qua nguyên văn để curl, nhưng những vấn đề tương tự sẽ xảy ra. Ví dụ trên sẽ không hoạt động (nó sẽ mở rộng đến tên tập tin trong thư mục hiện tại) và sẽ không \*. Bạn cũng không thể báo giá $curl_opts vì nó sẽ được công nhận là một tùy chọn (không hợp lệ) thành curl.

curl: option -s --noproxy * -O: is unknown 
curl: try 'curl --help' or 'curl --manual' for more information 

Vì vậy, tôi muốn giới thiệu việc sử dụng các bash biến $GLOBIGNORE để ngăn chặn sự bành trướng filename hoàn toàn nếu áp dụng cho mô hình toàn cầu, hoặc sử dụng các set -f built-in cờ.

#!/bin/bash 
GLOBIGNORE="*" 
curl_opts="-s --noproxy * -O" 
curl $curl_opts "$1" ## no filename expansion 

Áp dụng lại ví dụ ban đầu của bạn:

me$ FOO="BAR * BAR" 

me$ echo $FOO 
BAR file1 file2 file3 file4 BAR 

me$ set -f 
me$ echo $FOO 
BAR * BAR 

me$ set +f 
me$ GLOBIGNORE=* 
me$ echo $FOO 
BAR * BAR 
+2

Chỉ 'set -f' làm việc trong trường hợp của tôi –

+2

Giải thích tuyệt vời, cảm ơn! Usecase của tôi là' SELECT * FROM etc.', điều này là cách duy nhất hoạt động – knutole

+0

Cảm ơn bạn đã trình bày giải pháp set -f! – hachre

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