$ VAR so với $ {VAR} và trích dẫn hay không trích dẫn


139

tôi có thể viết

VAR=$VAR1
VAR=${VAR1}
VAR="$VAR1"
VAR="${VAR1}"

kết quả cuối cùng đối với tôi dường như giống nhau. Tại sao tôi nên viết cái này hay cái kia? có ai trong số này không di động / POSIX không?

Câu trả lời:


99

VAR=$VAR1là một phiên bản đơn giản hóa của VAR=${VAR1}. Có những thứ thứ hai có thể làm mà thứ nhất không thể, ví dụ, tham chiếu một chỉ mục mảng (không di động) hoặc loại bỏ một chuỗi con (POSIX-Portable). Xem phần Thêm về các biến của Hướng dẫn Bash cho Người mới bắt đầu và Mở rộng tham số trong thông số POSIX.

Sử dụng dấu ngoặc kép quanh một biến như trong rm -- "$VAR1"hoặc rm -- "${VAR}"là một ý tưởng tốt. Điều này làm cho nội dung của biến là một đơn vị nguyên tử. Nếu giá trị biến chứa khoảng trống (tốt, các ký tự trong $IFSbiến đặc biệt, khoảng trống theo mặc định) hoặc ký tự toàn cầu và bạn không trích dẫn nó, thì mỗi từ được xem xét để tạo tên tệp (globalbing) có phần mở rộng tạo ra nhiều đối số cho bất kỳ bạn đang làm

$ find .
.
./*r*
./-rf
./another
./filename
./spaced filename
./another spaced filename
./another spaced filename/x
$ var='spaced filename'
# usually, 'spaced filename' would come from the output of some command and you weren't expecting it
$ rm $var
rm: cannot remove 'spaced': No such file or directory
# oops! I just ran 'rm spaced filename'
$ var='*r*'
$ rm $var
# expands to: 'rm' '-rf' '*r*' 'another spaced filename'

$ find .
.
./another
./spaced filename
./another spaced filename
$ var='another spaced filename'
$ rm -- "$var"
$ find .
.
./another
./spaced filename

Về tính di động: Theo POSIX.1-2008, mục 2.6.2 , các dấu ngoặc nhọn là tùy chọn.


@shawn đã cập nhật câu hỏi của tôi vì tôi cũng tò mò về tính di động
xenoterracide

@shawn: Tôi nghi ngờ ví dụ của bạn là hợp lệ. Bạn có bất kỳ ví dụ thực tế nào về shell mà var1=$varviệc mở rộng gây ra lỗi không?
alex

@alex: Cảm ơn. Tôi nghĩ rằng tôi đã thử nghiệm nó trên dòng lệnh, nhưng tôi đã làm sai. Tôi đã thay đổi ví dụ.
Shawn J. Goff

Với ví dụ được cập nhật, tốt hơn hết là bạn nên nhớ rằng bạn thường muốn phiên bản được trích dẫn, vì ví dụ này là một trường hợp góc.
alex

9
@Shawn: Các trích dẫn không cần thiết trong một bài tập. Chúng là cần thiết trong hầu hết các sử dụng khác, bao gồm export VAR=$VAR1. Đối với các dấu ngoặc nhọn, chúng là tùy chọn (kiểm tra đoạn thứ tư của phần bạn đã trích dẫn; đây là trường hợp trong tất cả các vỏ POSIX và POSIX trước).
Gilles

60

${VAR}$VARhoàn toàn tương đương. Đối với việc mở rộng biến đơn giản, lý do duy nhất để sử dụng ${VAR}là khi phân tích cú pháp sẽ lấy quá nhiều ký tự vào tên biến, như trong ${VAR1}_$VAR2(không có dấu ngoặc sẽ tương đương với ${VAR1_}$VAR2). Hầu hết các bản mở rộng được tô điểm ( ${VAR:=default},, ${VAR#prefix}) đều yêu cầu niềng răng.

Trong một phép gán biến, việc tách trường (nghĩa là tách ở khoảng trắng trong giá trị) và mở rộng tên đường dẫn (nghĩa là toàn cầu) bị tắt, do đó, VAR=$VAR1chính xác tương đương với VAR="$VAR1", trong tất cả các vỏ POSIX và trong tất cả các POSIX trước mà tôi đã nghe nói . (POSIX ref: các lệnh đơn giản ). Vì lý do tương tự, VAR=*đáng tin cậy đặt VARchuỗi ký tự *; tất nhiên VAR=a bđặt VARđể akể từ khi blà một từ riêng biệt ở nơi đầu tiên. Nói chung, dấu ngoặc kép là không cần thiết khi cú pháp shell mong đợi một từ duy nhất, ví dụ như trongcase … in (nhưng không phải trong mẫu), nhưng ngay cả ở đó bạn cũng cần cẩn thận: ví dụ POSIX chỉ định rằngmục tiêu chuyển hướng ( >$filename) không yêu cầu trích dẫn trong tập lệnh, nhưng một số shell bao gồm bash không yêu cầu dấu ngoặc kép ngay cả trong tập lệnh. Xem Khi nào cần trích dẫn kép? để phân tích kỹ lưỡng hơn.

Bạn cần có dấu ngoặc kép trong các trường hợp khác, đặc biệt là export VAR="${VAR1}"(có thể viết tương đương export "VAR=${VAR1}") trong nhiều shell (POSIX để trường hợp này mở). Sự giống nhau của trường hợp này với các bài tập đơn giản và tính chất phân tán của danh sách các trường hợp bạn không cần dấu ngoặc kép, là lý do tại sao tôi khuyên bạn chỉ nên sử dụng dấu ngoặc kép trừ khi bạn muốn tách và toàn cầu.


2
Theo nguyên tắc chung, tôi sẽ luôn trích dẫn các mở rộng biến ngay cả khi tôi biết giá trị sẽ không chứa bất kỳ IFSký tự nào vì tôi muốn theo thói quen. Một ngoại lệ là tôi không trích dẫn giá trị khi thực hiện gán biến (trừ khi được yêu cầu, chẳng hạn như khi giá trị chứa khoảng trắng). Điều này làm cho cú pháp trình chỉnh sửa làm nổi bật hữu ích hơn khi có các lệnh thay thế như FOO=$(BAR=$(BAZ=blah; printf %s "${BAZ}"); printf %s "${BAR}"). Thay vì tô màu mọi thứ màu "chuỗi", tôi nhận được cú pháp tô sáng mã được lồng. Đây cũng là lý do tại sao tôi tránh backticks.
Richard Hansen

Mặc dù >$filecác tập lệnh POSIX vẫn ổn, nhưng nó không bị lỗi ngay cả khi không tương tác (trừ khi việc tuân thủ POSIX được thi hành với $POSIXLY_CORRECThoặc --posix...).
Stéphane Chazelas

Mặc dù sự thật là trích dẫn không cần thiết VAR=$VAR1, đôi khi tôi đã ngạc nhiên bởi local VAR=$VAR1, điều mà tôi nhớ là hoạt động khác nhau ở một số khía cạnh, trong ít nhất là một số vỏ. Nhưng atm, tôi không thể tái tạo sự khác biệt.
dubiousjim

Ok, tìm thấy vấn đề tôi đang nhớ . Nó chỉ xuất hiện trong một số vỏ.
dubiousjim

@dubiousjim local VAR=$VAR1là như thế export VAR=$VAR1, nó phụ thuộc vào vỏ.
Gilles

8

Bảng báo giá

Hãy xem xét rằng trích dẫn kép được sử dụng để mở rộng biến đổi và trích dẫn đơn được sử dụng để trích dẫn mạnh, tức là mở rộng sans.

Sự bành trướng:

this='foo'
that='bar'
these="$this"
those='$that'

Đầu ra:

for item in "$this" "$that" "$these" "$those"; do echo "$item"; done
foo
bar
foo
$that

Có thể đáng để đề cập rằng bạn nên sử dụng trích dẫn bất cứ khi nào có thể vì một số lý do, trong số đó tốt nhất là nó được coi là thực tiễn tốt nhất và dễ đọc. Ngoài ra bởi vì Bash đôi khi kỳ quặc và thường cho những cách dường như phi logic hoặc không hợp lý / bất ngờ, và thay đổi trích dẫn ngầm định kỳ vọng thành rõ ràng, làm giảm bề mặt lỗi đó (hoặc tiềm năng - cho nó).

Và mặc dù nó hoàn toàn hợp pháp để không trích dẫn, và sẽ hoạt động trong hầu hết các trường hợp, chức năng đó được cung cấp để thuận tiện và có lẽ ít di động hơn. thực hành chính thức đầy đủ được đảm bảo để phản ánh ý định và kỳ vọng là trích dẫn.

Thay thế

Bây giờ cũng xem xét rằng cấu trúc "${somevar}"được sử dụng cho các hoạt động thay thế. Một số trường hợp sử dụng, chẳng hạn như thay thế và mảng.

Thay thế (tước):

thisfile='foobar.txt.bak'
foo="${thisfile%.*}"   # removes shortest part of value in $thisfile matching after '%' from righthand side
bar="${thisfile%%.*}"  # removes longest matching

for item in "$foo" "$bar"; do echo "$item"; done
foobar.txt
foobar

Thay thế (thay thế):

foobar='Simplest, least effective, least powerful'
# ${var/find/replace_with}
foo="${foobar/least/most}"   #single occurrence
bar="${foobar//least/most}"  #global occurrence (all)

for item in "$foobar" "$foo" "$bar"; do echo "$item"; done
Simplest, least effective, least powerful
Simplest, most effective, least powerful
Simplest, most effective, most powerful

Mảng:

mkdir temp
# create files foo.txt, bar.txt, foobar.txt in temp folder
touch temp/{foo,bar,foobar}.txt
# alpha is array of output from ls  
alpha=($(ls temp/*))

echo "$alpha"         #  temp/foo.txt
echo "${alpha}"       #  temp/foo.txt
echo "${alpha[@]}"    #  temp/bar.txt  temp/foobar.txt  temp/foo.txt
echo "${#alpha}"      #  12 # length of first element (implicit index [0])
echo "${#alpha[@]}"   #  3  # number of elements
echo "${alpha[1]}"    #  temp/foobar.txt # second element
echo "${#alpha[1])"   #  15 # length of second element

for item in "${alpha[@]}"; do echo "$item"; done
temp/bar.txt
temp/foobar.txt
temp/foo.txt

Tất cả điều này hầu như không làm trầy xước bề mặt của "${var}"cấu trúc thay thế. Tham chiếu dứt khoát cho kịch bản lệnh shell Bash là tham chiếu trực tuyến miễn phí, TLDP Dự án Tài liệu Linuxhttps://www.tldp.org/LDP/abs/html/parameter-substitution.html


1
rất nhiều thông tin.
orion elenzil

0
ls -la

lrwxrwxrwx.  1 root root      31 Nov 17 13:13 prodhostname
lrwxrwxrwx.  1 root root      33 Nov 17 13:13 testhostname
lrwxrwxrwx.  1 root root      32 Nov 17 13:13 justname

kết thúc sau đó:

env=$1
    if [ ! -f /dirname/${env}hostname ]

đáng nói đến như một ví dụ rõ ràng hơn về việc sử dụng curlies

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.