Khi nào chúng ta cần niềng răng quanh các biến vỏ?


657

Trong shell script, khi nào chúng ta sử dụng {}khi mở rộng biến?

Ví dụ, tôi đã thấy như sau:

var=10        # Declare variable

echo "${var}" # One use of the variable
echo "$var"   # Another use of the variable

Có một sự khác biệt đáng kể, hoặc nó chỉ là phong cách? Là một trong những ưa thích hơn khác?

Câu trả lời:


749

Trong ví dụ cụ thể này, nó không có sự khác biệt. Tuy nhiên, {}trong ${}rất hữu ích nếu bạn muốn mở rộng biến footrong chuỗi

"${foo}bar"

"$foobar"thay vào đó sẽ mở rộng biến được xác định bởi foobar.

Niềng răng xoăn cũng được yêu cầu vô điều kiện khi:

  • mở rộng các phần tử mảng, như trong ${array[42]}
  • sử dụng các hoạt động mở rộng tham số, như trong ${filename%.*}(loại bỏ phần mở rộng)
  • mở rộng các tham số vị trí ngoài 9: "$8 $9 ${10} ${11}"

Làm điều này ở mọi nơi, thay vì chỉ trong các trường hợp mơ hồ, có thể được coi là thực hành lập trình tốt. Điều này là cho cả tính nhất quán và để tránh những bất ngờ như $foo_$bar.jpg, trong đó không rõ ràng về mặt trực quan mà phần gạch dưới trở thành một phần của tên biến.


106
{}được gọi là mở rộng cú đúp . ${}được gọi là mở rộng biến. Họ làm những việc khác nhau. Tôi sẽ nâng cấp bạn ngoại trừ bit không mở rộng.
Spencer Rathbun

5
@NewUser " Vì vậy, ngoài các mảng không thực sự cần thiết " Không phải vậy, các dấu ngoặc nhọn là cần thiết cho MỞ RỘNG PARAMETER , một cấu trúc rất hữu ích trong kịch bản. Tôi đã thấy nhiều tập lệnh sed và awk có thể được thay thế bằng một chút mở rộng tham số.
Cuộc bao vây

10
@caffinedmonkey $()được sử dụng để thực thi một lệnh, như vậy md5sum=$(md5sum foo.bin)sẽ lưu trữ đầu ra của md5sum foo.binbiến md5sum, bây giờ có thể truy cập bằng cách sử dụng ${md5sum}. Ngoài ra, +1 và nhiều hơn nữa về tinh thần đối với OP khi đề cập rằng đó là cách thực hành tốt để được rõ ràng!
L0j1k

11
@ L0j1k Nói về minh bạch, tôi thấy nó quan trọng đề cập đến $()thực thi lệnh từ một subshell .
Adrian Günter

2
@karatedog ${1:-20}là một hình thức mở rộng tham số. Ở đây không rõ ràng vì nó chủ yếu sử dụng các chữ số và toán tử số học để lừa chúng ta nghĩ rằng có liên quan đến số học, nhưng nó thực sự đề cập đến tham số vị trí $1, nếu không được xác định sẽ được thay thế bằng một giá trị mặc định 20(cú pháp là ${variable:-default_value}).
Aaron

126

Các biến được khai báo và gán mà không có $và không có {}. Bạn phải sử dụng

var=10

để phân công. Để đọc từ biến (nói cách khác, 'mở rộng' biến), bạn phải sử dụng $.

$var      # use the variable
${var}    # same as above
${var}bar # expand var, and append "bar" too
$varbar   # same as ${varbar}, i.e expand a variable called varbar, if it exists.

Điều này đôi khi làm tôi bối rối - trong các ngôn ngữ khác, chúng tôi đề cập đến biến theo cùng một cách, bất kể nó ở bên trái hay bên phải của bài tập. Nhưng shell-script thì khác, $var=10không làm những gì bạn nghĩ!


34

Bạn sử dụng {}để nhóm. Niềng răng được yêu cầu để bổ sung các yếu tố mảng. Thí dụ:

dir=(*)           # store the contents of the directory into an array
echo "${dir[0]}"  # get the first entry.
echo "$dir[0]"    # incorrect

Tôi không thể hiểu dòng đầu tiên dir=(*). Theo tôi biết, dirlà một lệnh được xây dựng để liệt kê nội dung thư mục (tương đương ls -C -b). Bạn có thể vui lòng giải thích?
Jarvis

1
Trong lập trình shell, các lệnh và đối số phải được tách biệt với nhau bằng khoảng trắng. Ở đây, bạn thấy dấu bằng không có khoảng trắng, nghĩa là đây là một phép gán biến. dirlà tên của biến và dấu ngoặc đơn được sử dụng để thu thập mở rộng tên tệp *thành một mảng.
glenn jackman

27

Bạn cũng có thể thực hiện một số thao tác văn bản bên trong dấu ngoặc nhọn:

STRING="./folder/subfolder/file.txt"
echo ${STRING} ${STRING%/*/*}

Kết quả:

./folder/subfolder/file.txt ./folder

hoặc là

STRING="This is a string"
echo ${STRING// /_}

Kết quả:

This_is_a_string

Bạn không đúng trong "các biến thông thường" là không cần thiết ... Nhưng nó hữu ích hơn cho việc gỡ lỗi và đọc một tập lệnh.


11

Phần cuối của tên biến thường được biểu thị bằng khoảng trắng hoặc dòng mới. Nhưng nếu chúng ta không muốn có một khoảng trắng hoặc dòng mới sau khi in giá trị biến thì sao? Các dấu ngoặc nhọn báo cho trình thông dịch shell nơi kết thúc của tên biến.

Ví dụ cổ điển 1) - biến shell không có dấu trắng

TIME=10

# WRONG: no such variable called 'TIMEsecs'
echo "Time taken = $TIMEsecs"

# What we want is $TIME followed by "secs" with no whitespace between the two.
echo "Time taken = ${TIME}secs"

Ví dụ 2) Đường dẫn Java với các tệp được phiên bản

# WRONG - no such variable LATESTVERSION_src
CLASSPATH=hibernate-$LATESTVERSION_src.zip:hibernate_$LATEST_VERSION.jar

# RIGHT
CLASSPATH=hibernate-${LATESTVERSION}_src.zip:hibernate_$LATEST_VERSION.jar

(Câu trả lời của Fred đã nêu rõ điều này nhưng ví dụ của anh ấy hơi quá trừu tượng)


5

Niềng răng xoăn luôn cần thiết để truy cập các phần tử mảng và thực hiện mở rộng niềng răng.

Thật tốt khi không quá thận trọng và sử dụng {}để mở rộng biến vỏ ngay cả khi không có phạm vi cho sự mơ hồ.

Ví dụ:

dir=log
prog=foo
path=/var/${dir}/${prog}      # excessive use of {}, not needed since / can't be a part of a shell variable name
logfile=${path}/${prog}.log   # same as above, . can't be a part of a shell variable name
path_copy=${path}             # {} is totally unnecessary
archive=${logfile}_arch       # {} is needed since _ can be a part of shell variable name

Vì vậy, tốt hơn là viết ba dòng như:

path=/var/$dir/$prog
logfile=$path/$prog.log
path_copy=$path

mà chắc chắn là dễ đọc hơn

Vì một tên biến không thể bắt đầu bằng một chữ số, shell không cần {}xung quanh các biến được đánh số (như $1, $2v.v.) trừ khi mở rộng như vậy được theo sau bởi một chữ số. Điều đó quá tinh tế và nó được sử dụng rõ ràng {}trong các bối cảnh như vậy:

set app      # set $1 to app
fruit=$1le   # sets fruit to apple, but confusing
fruit=${1}le # sets fruit to apple, makes the intention clear

Xem:


1
It's good to be not over-cautious: Tôi tự hỏi hầu hết mọi người nghĩ gì. Sử dụng dấu ngoặc nhọn mọi lúc để bạn không quên chúng khi cần hoặc chỉ sử dụng chúng khi cần thiết để cải thiện khả năng đọc.
Roger Dahl

1
Tôi nghĩ rằng chính sự thiếu nhận thức đã dẫn đến việc các lập trình viên sử dụng curlies ngay cả khi không cần thiết. Sự thiếu hiểu biết này tương tự như sai lầm phổ biến khác là không sử dụng dấu ngoặc kép để ngăn chặn việc chia tách hoặc vô tình từ. Trên cơ sở của nó, thực tế là các lập trình viên không nghiêm túc về kịch bản shell nhiều như các ngôn ngữ kịch bản lệnh khác như Python và Ruby.
codeforester

1
Đúng là. Thú cưng của tôi là mọi người dường như nghĩ rằng tất cả các biến nên là tất cả các mũ trong tập lệnh shell :)
Roger Dahl

2

Theo gợi ý của SierraX và Peter về thao tác văn bản {}, ví dụ , dấu ngoặc nhọn được sử dụng để truyền một biến cho một lệnh:

Giả sử bạn có tệp sposi.txt chứa dòng đầu tiên của một cuốn tiểu thuyết nổi tiếng của Ý:

> sposi="somewhere/myfolder/sposi.txt"
> cat $sposi

Ouput: quel ramo del lago di como che volge a mezzogiorno

Bây giờ tạo hai biến:

# Search the 2nd word found in the file that "sposi" variable points to
> word=$(cat $sposi | cut -d " " -f 2)

# This variable will replace the word
> new_word="filone"

Bây giờ thay thế nội dung biến từ bằng một trong new_word , bên trong tệp sposi.txt

> sed -i "s/${word}/${new_word}/g" $sposi
> cat $sposi

Ouput: quel filone del lago di como che volge a mezzogiorno

Từ "ramo" đã được thay thế.


1
Điều này cũng hoạt động tốt mà không có dấu ngoặc nhọn xung quanh các biến.
Armali

Bạn có thể muốn sửa lỗi weel-known novelbit. Nâng cao tuy nhiên.
gsl
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.