Câu trả lời:
CÂU TRẢ LỜI NGẮN
Giống như những người khác đã nói - bạn nên luôn trích dẫn các biến để ngăn chặn hành vi lạ. Vì vậy, hãy sử dụng echo "$ foo" thay vì chỉ echo $ foo .
CÂU TRẢ LỜI DÀI
Tôi nghĩ rằng ví dụ này đảm bảo giải thích thêm bởi vì có nhiều điều đang diễn ra hơn là nó có vẻ như trên khuôn mặt của nó.
Tôi có thể thấy sự nhầm lẫn của bạn đến từ đâu bởi vì sau khi bạn chạy ví dụ đầu tiên, bạn có thể tự nghĩ rằng cái vỏ rõ ràng đang làm:
Vì vậy, từ ví dụ đầu tiên của bạn:
me$ FOO="BAR * BAR"
me$ echo $FOO
Sau khi mở rộng tham số tương đương với:
me$ echo BAR * BAR
Và sau khi mở rộng tên tệp 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 chúng là tương đương.
Vì vậy, bạn có thể tự nghĩ "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"
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ố nên tương đương với:
me$ echo BAR \* BAR
Và sau khi mở rộng tên tệp nên 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 bị ngăn chặn bởi lối thoát.
Vậy tại sao sử dụng $ foo không hoạt động?
Đó là bởi vì có một bản mở rộng thứ ba diễn ra - Trích dẫn Loại bỏ. Từ bash thủ công loại bỏ trích dẫn là:
Sau các lần mở rộng trước đó, tất cả các lần xuất hiện không được trích dẫn của các ký tự '\', '' 'và' "'không xuất phát từ một trong các lần mở rộng trên đều bị xóa.
Vì vậy, điều xảy ra là khi bạn nhập lệnh trực tiếp vào dòng lệnh, ký tự thoát không phải là kết quả của lần mở rộng trước đó, vì vậy BASH sẽ xóa nó trước khi gửi nó tới lệnh echo, nhưng trong ví dụ thứ 2, "\ *" là kết quả của việc mở rộng Thông số trước đó, vì vậy nó KHÔNG bị xóa. 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ằng Trích dẫn loại bỏ.
Tôi hy vọng điều này có ý nghĩa. Cuối cùng, kết luận giống nhau - 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à logic sẽ hoạt động nếu chỉ mở rộng Parameter và Filename, không hoạt động.
Để được giải thích đầy đủ về việc mở rộng BASH, hãy tham khảo:
http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions
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"
Tuy nhiên, tôi đã gặp vấn đề ngay cả với cú pháp này. Hãy xem xét các 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ẽ phát sinh. Ví dụ trên sẽ không hoạt động (nó sẽ mở rộng thành tên tệp trong thư mục hiện tại) và cả sẽ không \*
. Bạn cũng không thể trích dẫn $curl_opts
vì nó sẽ được công nhận là một tùy chọn (không hợp lệ) curl
.
curl: option -s --noproxy * -O: is unknown
curl: try 'curl --help' or 'curl --manual' for more information
Do đó, tôi khuyên bạn nên sử dụng bash
biến $GLOBIGNORE
để ngăn chặn việc mở rộng tên tệp hoàn toàn nếu được áp dụng cho mẫu toàn cục hoặc sử dụng set -f
cờ tích hợp.
#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1" ## no filename expansion
Áp dụng cho 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
SELECT * FROM etc.
, đây là cách duy nhất hoạt động.
FOO='BAR * BAR'
echo "$FOO"
echo "$FOO"
Có thể đáng để tập thói quen sử dụng printf
thay vì 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"