Trong tập lệnh shell, nếu tôi xác định một biến như FOO=25
, có sự khác biệt nào giữa việc tham chiếu nó là $FOO$
và ${FOO}$
không?
Trong tập lệnh shell, nếu tôi xác định một biến như FOO=25
, có sự khác biệt nào giữa việc tham chiếu nó là $FOO$
và ${FOO}$
không?
Câu trả lời:
Trong hầu hết các tình huống cả hai $var
và ${var}
đều giống nhau. (Lưu ý rằng bạn không được sử dụng $
cuối cùng!)
Một ví dụ mà bạn cần niềng răng xoăn là khi bạn cần đặt một biến trong một chuỗi liên tục.
Thí dụ:
var=hel
echo ${var}lo
sẽ đầu ra hello
.
Để trả lời câu hỏi chính của bạn, ${foo}
được gọi là "mở rộng tham số" . Nói chính xác, $
chính nó bắt đầu mở rộng tham số, { }
thực sự là tùy chọn theo đặc tả POSIX , nhưng có thể hữu ích để chỉ ra phần cuối của tên biến:
$ foo="bar"
$ echo $fooBaz ## fails, no variable named $fooBaz exists
$ echo ${foo}Baz ## works, $foo is expanded and the string Baz is appended
barBaz
Về cơ bản, $foo
và ${foo}
giống hệt nhau. Ngoài các trường hợp như trên hoặc khi bạn đang thực hiện thao tác chuỗi , chúng hoàn toàn tương đương.
Tuy nhiên, bạn không nên thực sự sử dụng một trong số chúng. Nguyên tắc chung là, với rất ít ngoại lệ, bạn nên luôn luôn sử dụng "$foo"
hoặc "${foo}"
và không bao giờ $foo
hoặc ${foo}
. Bạn phải luôn trích dẫn các biến của mình để tránh gọi toán tử split + global (sẽ nói thêm về điều đó sau). Bạn chắc chắn không muốn $foo$
. Trận chung kết $
là không liên quan:
$ foo="bar"
$ echo "$foo$"
bar$
Vì vậy, trong khi các biến không được trích dẫn đôi khi vẫn ổn:
$ echo $foo
bar
Chúng thường không và thực sự nên tránh:
$ if [ -n $foo ]; then echo empty; else echo "not empty"; fi ## fails
empty
$ if [ -n "$foo" ]; then echo empty; else echo "not empty"; fi ## works
not empty
Lưu ý rằng dấu ngoặc cũng không giúp được gì ở đây:
$ if [ -n ${foo} ]; then echo empty; else echo "not empty"; fi ## fails
empty
$ if [ -n "${foo}" ]; then echo empty; else echo "not empty"; fi ## works
not empty
Khi bạn sử dụng $foo
hoặc ${foo}
, shell sẽ phân chia giá trị được lưu trong biến trên khoảng trắng (điều này có thể được thay đổi bằng cách đặt IFS
biến thành một thứ khác) thành một danh sách và sau đó, mỗi phần tử của danh sách được coi là một mẫu hình cầu và được mở rộng thành bất kỳ tập tin hoặc thư mục phù hợp. Điều này được gọi là toán tử chia + toàn cầu . Để minh họa, hãy xem xét một thư mục có hai tệp:
$ ls -l
-rw-r--r-- 1 terdon terdon 0 Oct 9 18:16 file1
-rw-r--r-- 1 terdon terdon 0 Oct 9 18:16 file2
Bây giờ, hãy đặt một biến thành foo *
:
$ foo="foo *"
Điều gì xảy ra nếu chúng ta cố gắng kiểm tra xem một tệp có tên đó tồn tại không?
$ if [ -e $foo ]; then echo "file exists"; else echo "no such file"; fi
file exists
Biến được chia thành foo
và *
, vì *
là ký tự đại diện khớp với bất kỳ chuỗi nào, trình bao cho bạn biết rằng một tệp có tên là foo *
exxists. Tuy nhiên, nếu chúng tôi trích dẫn chính xác, điều này sẽ không xảy ra:
$ if [ -e "$foo" ]; then echo "file exists"; else echo "no such file"; fi
no such file
Đó là một ví dụ tầm thường để minh họa điểm. Hãy tưởng tượng nếu tôi đã sử dụng rm
thay vì echo
mặc dù.
Vì vậy, quy tắc đầu tiên: luôn luôn trích dẫn các biến của bạn. Bạn có thể sử dụng một trong hai "$foo"
hoặc "${foo}"
nhưng trích dẫn một trong hai cách. Để biết thêm chi tiết về việc sử dụng các biến một cách an toàn, hãy xem các bài đăng sau:
$ var="foo *"
nên $ foo="foo *"
? Bạn không sử dụng var
bất cứ nơi nào.
$
cuối cùng! Nó sẽ được coi là ký tự bình thường và không phải là một phần của tên biến.