Đầu tiên, tách zsh khỏi phần còn lại. Đây không phải là vấn đề của vỏ cũ và hiện đại: zsh hành xử khác nhau. Các nhà thiết kế zsh đã quyết định làm cho nó không tương thích với các vỏ truyền thống (Bourne, ksh, bash), nhưng dễ sử dụng hơn.
Thứ hai, việc sử dụng dấu ngoặc kép mọi lúc sẽ dễ dàng hơn nhiều so với việc nhớ khi cần. Chúng cần thiết hầu hết thời gian, vì vậy bạn sẽ cần học khi không cần thiết, không phải khi cần.
Tóm lại, trích dẫn kép là cần thiết ở bất cứ nơi nào một danh sách các từ hoặc một mẫu được mong đợi . Chúng là tùy chọn trong các ngữ cảnh trong đó trình phân tích cú pháp dự kiến sẽ có một chuỗi thô.
Điều gì xảy ra mà không có dấu ngoặc kép
Lưu ý rằng không có dấu ngoặc kép, hai điều xảy ra.
- Đầu tiên, kết quả của việc mở rộng (giá trị của biến cho một thay thế tham số như
${foo}
, hoặc đầu ra của lệnh thay thế lệnh như thế $(foo)
) được chia thành các từ bất cứ nơi nào nó chứa khoảng trắng.
Chính xác hơn, kết quả của việc mở rộng được phân chia tại mỗi ký tự xuất hiện trong giá trị của IFS
biến (ký tự phân cách). Nếu một chuỗi các ký tự phân cách chứa khoảng trắng (dấu cách, tab hoặc dòng mới), thì khoảng trắng được tính là một ký tự đơn; phân cách không dẫn đầu, theo dõi hoặc lặp đi lặp lại dẫn đến các trường trống. Ví dụ: với IFS=" :"
, :one::two : three: :four
tạo ra các trường trống trước one
, giữa one
và two
và (một cái duy nhất) giữa three
và four
.
- Mỗi trường có kết quả từ việc chia tách được hiểu là một hình cầu (một mẫu ký tự đại diện) nếu nó chứa một trong các ký tự
\[*?
. Nếu mẫu đó khớp với một hoặc nhiều tên tệp, mẫu đó được thay thế bằng danh sách tên tệp phù hợp.
Một mở rộng biến không được trích dẫn $foo
thường được gọi là toán tử chia tách + Toán tử toàn cầu, ngược lại "$foo"
, chỉ lấy giá trị của biến foo
. Điều tương tự cũng xảy ra đối với thay thế lệnh: "$(foo)"
là thay thế lệnh, $(foo)
là thay thế lệnh theo sau là split + global.
Nơi bạn có thể bỏ qua dấu ngoặc kép
Dưới đây là tất cả các trường hợp tôi có thể nghĩ về trình bao kiểu Bourne nơi bạn có thể viết một biến hoặc thay thế lệnh mà không cần dấu ngoặc kép và giá trị được hiểu theo nghĩa đen.
Ở phía bên phải của một bài tập.
var=$stuff
a_single_star=*
Lưu ý rằng bạn cần có dấu ngoặc kép sau export
, bởi vì đó là một nội dung thông thường, không phải từ khóa. Điều này chỉ đúng trong một số shell như dash, zsh (trong mô phỏng sh), yash hoặc posh; bash và ksh đều đối xử export
đặc biệt.
export VAR="$stuff"
Trong một case
tuyên bố.
case $var in …
Lưu ý rằng bạn cần dấu ngoặc kép trong một mẫu trường hợp. Chia tách từ không xảy ra trong một mẫu trường hợp, nhưng một biến không được trích dẫn được hiểu là một mẫu trong khi một biến được trích dẫn được hiểu là một chuỗi bằng chữ.
a_star='a*'
case $var in
"$a_star") echo "'$var' is the two characters a, *";;
$a_star) echo "'$var' begins with a";;
esac
Trong dấu ngoặc kép. Dấu ngoặc kép là cú pháp đặc biệt shell.
[[ -e $filename ]]
Ngoại trừ việc bạn làm cần dấu ngoặc kép mà một mô hình hoặc biểu thức chính quy dự kiến: ở phía bên tay phải của =
hoặc ==
hoặc !=
hoặc =~
.
a_star='a*'
if [[ $var == "$a_star" ]]; then echo "'$var' is the two characters a, *"
elif [[ $var == $a_star ]]; then echo "'$var' begins with a"
fi
Bạn cần có dấu ngoặc kép như bình thường trong dấu ngoặc đơn [ … ]
vì chúng là cú pháp shell thông thường (đó là một lệnh xảy ra để được gọi [
). Xem dấu ngoặc đơn hoặc dấu ngoặc kép
Trong một chuyển hướng trong các vỏ POSIX không tương tác (không bash
, cũng không ksh88
).
echo "hello world" >$filename
Một số shell, khi tương tác, sẽ coi giá trị của biến là mẫu ký tự đại diện. POSIX nghiêm cấm hành vi đó trong các vỏ không tương tác, nhưng một số vỏ bao gồm bash (ngoại trừ ở chế độ POSIX) và ksh88 (bao gồm cả khi được tìm thấy là POSIX (được cho là) sh
của một số Unice thương mại như Solaris) vẫn thực hiện việc đó ( bash
cũng cố gắng chia tách và chuyển hướng không trừ khi đó chia + globbing kết quả chính xác một từ), đó là lý do tại sao nó tốt hơn để trích dẫn mục tiêu của chuyển hướng trong một sh
kịch bản trong trường hợp bạn muốn chuyển đổi nó vào một bash
kịch bản một ngày nào đó, hoặc chạy nó trên một hệ thống mà sh
là không tuân thủ về điểm đó, hoặc nó có thể có nguồn gốc từ các vỏ tương tác.
Bên trong một biểu thức số học. Trong thực tế, bạn cần bỏ các dấu ngoặc kép để một biến được phân tích cú pháp dưới dạng biểu thức số học.
expr=2*2
echo "$(($expr))"
Tuy nhiên, bạn không cần các trích dẫn xung quanh việc mở rộng số học vì chúng có thể bị chia tách từ trong hầu hết các shell vì POSIX yêu cầu (!?).
Trong một mảng con liên kết.
typeset -A a
i='foo bar*qux'
a[foo\ bar\*qux]=hello
echo "${a[$i]}"
Một biến không được trích dẫn và thay thế lệnh có thể hữu ích trong một số trường hợp hiếm gặp:
- Khi giá trị biến hoặc đầu ra lệnh bao gồm một danh sách các mẫu hình cầu và bạn muốn mở rộng các mẫu này vào danh sách các tệp phù hợp.
- Khi bạn biết rằng giá trị không chứa bất kỳ ký tự đại diện nào, nó
$IFS
không được sửa đổi và bạn muốn phân tách nó ở các ký tự khoảng trắng.
- Khi bạn muốn phân tách một giá trị tại một ký tự nhất định: vô hiệu hóa toàn cầu với
set -f
, đặt thành IFS
ký tự dấu tách (hoặc để nó một mình để phân tách ở khoảng trắng), sau đó thực hiện mở rộng.
Zsh
Trong zsh, bạn có thể bỏ qua dấu ngoặc kép hầu hết các lần, với một vài ngoại lệ.
$var
không bao giờ mở rộng thành nhiều từ, tuy nhiên nó sẽ mở rộng ra danh sách trống (trái ngược với danh sách chứa một từ trống, đơn) nếu giá trị của var
là chuỗi rỗng. Tương phản:
var=
print -l $var foo # prints just foo
print -l "$var" foo # prints an empty line, then foo
Tương tự, "${array[@]}"
mở rộng cho tất cả các phần tử của mảng, trong khi $array
chỉ mở rộng thành các phần tử không trống.
Các @
cờ mở rộng tham số đôi khi đòi hỏi dấu ngoặc kép xung quanh toàn bộ thay: "${(@)foo}"
.
Thay thế lệnh trải qua quá trình phân tách trường nếu không được trích dẫn: echo $(echo 'a'; echo '*')
in a *
(với một khoảng trắng) trong khi echo "$(echo 'a'; echo '*')"
in chuỗi hai dòng không thay đổi. Sử dụng "$(somecommand)"
để có được đầu ra của lệnh trong một từ duy nhất, sans dòng mới cuối cùng. Sử dụng "${$(somecommand; echo _)%?}"
để có được đầu ra chính xác của lệnh bao gồm các dòng mới cuối cùng. Sử dụng "${(@f)$(somecommand)}"
để có được một mảng các dòng từ đầu ra của lệnh.
SH_WORD_SPLIT
tùy chọn.