Cách sử dụng dấu ngoặc kép hoặc dấu ngoặc đơn, dấu ngoặc đơn, dấu ngoặc nhọn


657

Tôi bối rối bởi việc sử dụng dấu ngoặc, dấu ngoặc đơn, dấu ngoặc nhọn trong Bash, cũng như sự khác biệt giữa các dạng kép hoặc đơn của chúng. Có một lời giải thích rõ ràng?

Câu trả lời:


605

Trong Bash, test[được xây dựng shell.

Dấu ngoặc kép , là một từ khóa shell, cho phép chức năng bổ sung. Ví dụ, bạn có thể sử dụng &&||thay vì -a-ovà có một nhà điều hành phù hợp với biểu thức chính quy =~.

Ngoài ra, trong một thử nghiệm đơn giản, dấu ngoặc vuông đôi dường như đánh giá khá nhanh hơn nhiều so với dấu ngoặc đơn.

$ time for ((i=0; i<10000000; i++)); do [[ "$i" = 1000 ]]; done

real    0m24.548s
user    0m24.337s
sys 0m0.036s
$ time for ((i=0; i<10000000; i++)); do [ "$i" = 1000 ]; done

real    0m33.478s
user    0m33.478s
sys 0m0.000s

Các dấu ngoặc nhọn, ngoài việc phân định một tên biến được sử dụng để mở rộng tham số để bạn có thể thực hiện những việc như:

  • Cắt bớt nội dung của một biến

    $ var="abcde"; echo ${var%d*}
    abc
  • Thay thế tương tự như sed

    $ var="abcde"; echo ${var/de/12}
    abc12
  • Sử dụng một giá trị mặc định

    $ default="hello"; unset var; echo ${var:-$default}
    hello
  • và nhiều hơn nữa

Ngoài ra, mở rộng dấu ngoặc tạo ra danh sách các chuỗi thường được lặp lại trong các vòng lặp:

$ echo f{oo,ee,a}d
food feed fad

$ mv error.log{,.OLD}
(error.log is renamed to error.log.OLD because the brace expression
expands to "mv error.log error.log.OLD")

$ for num in {000..2}; do echo "$num"; done
000
001
002

$ echo {00..8..2}
00 02 04 06 08

$ echo {D..T..4}
D H L P T

Lưu ý rằng các tính năng gia tăng và không hàng đầu không có sẵn trước Bash 4.

Cảm ơn gboffi đã nhắc nhở tôi về việc mở rộng niềng răng.

Dấu ngoặc kép được sử dụng cho các phép toán số học :

((a++))

((meaning = 42))

for ((i=0; i<10; i++))

echo $((a + b + (14 * c)))

và chúng cho phép bạn bỏ qua các ký hiệu đô la trên các biến số nguyên và mảng và bao gồm các khoảng trắng xung quanh các toán tử để dễ đọc.

Dấu ngoặc đơn cũng được sử dụng cho các chỉ số mảng :

array[4]="hello"

element=${array[index]}

Nẹp xoăn được yêu cầu cho các tham chiếu mảng (hầu hết / tất cả?) Ở phía bên tay phải.

Nhận xét của ephemient nhắc nhở tôi rằng dấu ngoặc đơn cũng được sử dụng cho subshells. Và chúng được sử dụng để tạo mảng.

array=(1 2 3)
echo ${array[1]}
2

8
CẢNH BÁO: Chức năng đó là một quả bom ngã ba, không chạy nó. Xem: en.wikipedia.org/wiki/Fork_bomb
Tạm dừng cho đến khi có thông báo mới.

3
Nó chỉ là một quả bom ngã ba nếu bạn gọi nó với một bổ sung :.
ephemient

7
Ngoài ra, để hoàn thiện, tôi đã bắt gặp điều này trong một kịch bản cũ : $[expression]; đây là cú pháp biểu thức số học cũ, không dùng nữa cho cú pháp ưa thích mới hơn:$((expression))
michael

2
@DennisWilliamson Một cách sử dụng dấu ngoặc nhọn khác bashlà tạo các chuỗi, như được đề cập ngoại vi bên dưới ( stackoverflow.com/a/8552128/2749397 ) Như tôi muốn bình luận một chút về tính năng này (như bạn đã không đề cập đến nó ;-) I ' m lấy tự do sử dụng câu trả lời được bình chọn nhiều nhất làm phương tiện ... Hai ví dụ về chuỗi chữ: echo {01..12}-> 01 02 03 04 05 06 07 08 09 10 11 12(lưu ý số không ban đầu); echo {C..Q}-> C D E F G H I J K L M N O P Q. Công dụng chính của nó là trong các vòng lặp, ví dụ: for cnt in {01..12} ; do ... ${cnt} ... ; done
gboffi

1
@gboffi: Tính năng đệm số 0 trở nên khả dụng trong Bash 4. Ngoài ra, trong Bash 4, bạn có thể chỉ định gia số theo trình tự: echo {01..12..2}-> "01 03 05 07 09 11". Cảm ơn đã nhắc nhở về trình tự. Tôi sẽ thêm nó vào câu trả lời của tôi.
Tạm dừng cho đến khi có thông báo mới.

335
  1. Một ngoặc đơn ( [) thường thực sự gọi một chương trình có tên [; man testhoặc man [để biết thêm. Thí dụ:

    $ VARIABLE=abcdef
    $ if [ $VARIABLE == abcdef ] ; then echo yes ; else echo no ; fi
    yes
  2. Dấu ngoặc kép ( [[) thực hiện cùng một thứ (về cơ bản) như một dấu ngoặc đơn, nhưng là một giá trị dựng sẵn.

    $ VARIABLE=abcdef
    $ if [[ $VARIABLE == 123456 ]] ; then echo yes ; else echo no ; fi
    no
  3. Dấu ngoặc đơn ( ()) được sử dụng để tạo một lớp con. Ví dụ:

    $ pwd
    /home/user 
    $ (cd /tmp; pwd)
    /tmp
    $ pwd
    /home/user

    Như bạn có thể thấy, lớp con cho phép bạn thực hiện các thao tác mà không ảnh hưởng đến môi trường của trình bao hiện tại.

  4. (a) Niềng răng ( {}) được sử dụng để xác định rõ ràng các biến. Thí dụ:

    $ VARIABLE=abcdef
    $ echo Variable: $VARIABLE
    Variable: abcdef
    $ echo Variable: $VARIABLE123456
    Variable:
    $ echo Variable: ${VARIABLE}123456
    Variable: abcdef123456

    (b) Niềng răng cũng được sử dụng để thực hiện một chuỗi các lệnh trong bối cảnh shell hiện tại , ví dụ

    $ { date; top -b -n1 | head ; } >logfile 
    # 'date' and 'top' output are concatenated, 
    # could be useful sometimes to hunt for a top loader )
    
    $ { date; make 2>&1; date; } | tee logfile
    # now we can calculate the duration of a build from the logfile

Có một sự khác biệt cú pháp tinh tế với ( ), mặc dù (xem tài liệu tham khảo bash ); về cơ bản, dấu chấm phẩy ;sau câu lệnh cuối cùng trong niềng răng là điều bắt buộc, và niềng răng {, } phải được bao quanh bởi không gian.


26
Chà, [thực ra là một nội trang trong Bash, nhưng nó được cho là hoạt động như /bin/[trái ngược với [[nội dung. [[có các tính năng khác nhau, như các hoạt động logic hơn và vai trò trích dẫn khác nhau. Ngoài ra: các dấu ngoặc đơn cũng được sử dụng cho các mảng, thay thế quá trình và các chuỗi mở rộng; dấu ngoặc kép được sử dụng cho số học; dấu ngoặc nhọn {}được sử dụng để nhóm lệnh hoặc vô số kiểu mở rộng tham số hoặc mở rộng dấu ngoặc hoặc mở rộng chuỗi. Tôi chắc chắn tôi cũng đã bỏ lỡ một số cách sử dụng khác ...
ephemient

4
Các đẳng thức kép trong biểu thức if [ $VARIABLE == abcdef ]là một bashism - mặc dù nó hoạt động - có lẽ nên tránh; hoặc sử dụng rõ ràng bash ( if [[ ...==...]]) hoặc làm rõ rằng bạn đang sử dụng điều kiện truyền thống hơn ( if [ "$VARIABLE" = "abcdef" ]). Có thể cho rằng, các tập lệnh nên bắt đầu đơn giản và dễ mang theo nhất có thể, cho đến khi chúng thực sự cần các tính năng dành riêng cho bash (vì lý do này hay lý do khác). Nhưng trong mọi trường hợp, ý định nên rõ ràng; "=" và "==" và "[[" và "[" hoạt động khác nhau và cách sử dụng của chúng phải nhất quán.
michael

3
@michael_n: +1 cho nhận xét này. Mặt khác, tôi thích kịch bản, nhưng tôi thấy khá lúng túng khi cách di động là kiểm tra qua [ "$var" = ".."]thay vì ==, trong khi ở C, nó sẽ chỉ định thay vì kiểm tra (và là một nguyên nhân phổ biến của lỗi) ... tại sao lại không 't testsử dụng ==thay vì =? có ai biết?
Olivier Dulac

Ngoài ra đây là một điều thú vị rằng (ít nhất là trên Kubfox) lệnh /usr/bin/[không phải là một liên kết tượng trưng cho /usr/bin/testvà hơn thế nữa: các chương trình này thậm chí có một vài kích cỡ khác nhau!
Hi-Angel

Ngoài ra: một dấu ngoặc đơn đóng )là một phần của casecú pháp câu lệnh để kết thúc một dòng trường hợp. Nó không có dấu ngoặc đơn mở. Điều này đã ném tôi đi ngay lần đầu tiên tôi nhìn thấy nó.
Agustín Amenabar

302

Chân đế

if [ CONDITION ]    Test construct  
if [[ CONDITION ]]  Extended test construct  
Array[1]=element1   Array initialization  
[a-z]               Range of characters within a Regular Expression
$[ expression ]     A non-standard & obsolete version of $(( expression )) [1]

[1] http://wiki.bash-hackers.org/scripting/obsolete

Dấu ngoặc nhọn

${variable}                             Parameter substitution  
${!variable}                            Indirect variable reference  
{ command1; command2; . . . commandN; } Block of code  
{string1,string2,string3,...}           Brace expansion  
{a..z}                                  Extended brace expansion  
{}                                      Text replacement, after find and xargs

Dấu ngoặc

( command1; command2 )             Command group executed within a subshell  
Array=(element1 element2 element3) Array initialization  
result=$(COMMAND)                  Command substitution, new style  
>(COMMAND)                         Process substitution  
<(COMMAND)                         Process substitution 

Dấu ngoặc kép

(( var = 78 ))            Integer arithmetic   
var=$(( 20 + 5 ))         Integer arithmetic, with variable assignment   
(( var++ ))               C-style variable increment   
(( var-- ))               C-style variable decrement   
(( var0 = var1<98?9:21 )) C-style ternary operation

@Yola, bạn có thể vui lòng giải thích chính xác $ (varname) không? Trong các dự án Apple Xcode, tôi có thể chỉ định đường dẫn tệp là đầu vào / đầu ra tập lệnh. nếu tôi chỉ định $ SRC_ROOT / myFile.txt hoặc $ {SRC_ROOT} /myFile.txt (SRC_ROOT var được xuất bởi hệ thống xây dựng) - không hoạt động. chỉ $ (SRC_ROOT) /myFile.txt hoạt động. Điều gì có thể là lý do? Tên var rõ ràng không phải là một lệnh?
Motti Shneor

1
@MottiShneor, trong trường hợp của bạn $(varname)không liên quan đến cú pháp bash. Nó là một phần của cú pháp Makefile .
Sasha

Không phải vậy - Xcode không được xây dựng bằng cách sử dụng makefile và các biến của nó là các biến môi trường. Các quy trình xây dựng hệ thống Xcode độc ​​quyền đọc các giá trị của các biến môi trường được xác định trước này. Các bước xây dựng tùy chỉnh chỉ là các kịch bản shell thông thường (bash hoặc khác) và có quyền truy cập vào cùng một vars.
Motti Shneor

@MottiShneor, ok, hãy tinh chỉnh: rất có thể nó là một phần của cú pháp xcconfig . Dù sao, $(varname)không có liên quan đến cú pháp bash trong trường hợp của bạn.
Sasha

Bạn không đề cập đến sự khác biệt giữa cấu trúc thử nghiệm và cấu trúc thử nghiệm mở rộng.
Nikos

23

Tôi chỉ muốn thêm chúng từ TLDP :

~:$ echo $SHELL
/bin/bash

~:$ echo ${#SHELL}
9

~:$ ARRAY=(one two three)

~:$ echo ${#ARRAY}
3

~:$ echo ${TEST:-test}
test

~:$ echo $TEST


~:$ export TEST=a_string

~:$ echo ${TEST:-test}
a_string

~:$ echo ${TEST2:-$TEST}
a_string

~:$ echo $TEST2


~:$ echo ${TEST2:=$TEST}
a_string

~:$ echo $TEST2
a_string

~:$ export STRING="thisisaverylongname"

~:$ echo ${STRING:4}
isaverylongname

~:$ echo ${STRING:6:5}
avery

~:$ echo ${ARRAY[*]}
one two one three one four

~:$ echo ${ARRAY[*]#one}
two three four

~:$ echo ${ARRAY[*]#t}
one wo one hree one four

~:$ echo ${ARRAY[*]#t*}
one wo one hree one four

~:$ echo ${ARRAY[*]##t*}
one one one four

~:$ echo $STRING
thisisaverylongname

~:$ echo ${STRING%name}
thisisaverylong

~:$ echo ${STRING/name/string}
thisisaverylongstring

18
Tâm trí echo ${#ARRAY}hiển thị ba, vì phần tử đầu tiên ARRAYchứa ba ký tự, không phải vì nó chứa ba phần tử! Để in số lượng phần tử sử dụng echo ${#ARRAY[@]}.
TrueY

@zeal ${TEST:-test}bằng $TESTnếu biến TESTtồn tại, nếu không, nó chỉ trả về chuỗi "test". Có một phiên bản khác thậm chí còn nhiều hơn: ${TEST:=test}--- cũng tương đương với $TESTnếu TEST tồn tại, nhưng bất cứ khi nào không có, nó sẽ tạo biến TESTvà gán giá trị "kiểm tra" và cũng trở thành giá trị của toàn bộ biểu thức.
Yêu xác suất

18

Sự khác biệt giữa thử nghiệm , [[[ được giải thích rất chi tiết trong BashFAQ .

Để cắt ngắn một câu chuyện dài: kiểm tra thực hiện cú pháp cũ, di động của lệnh. Trong hầu hết tất cả các shell (shell Bourne lâu đời nhất là ngoại lệ), [là một từ đồng nghĩa để kiểm tra (nhưng yêu cầu một đối số cuối cùng là]). Mặc dù tất cả các shell hiện đại đều có các triển khai tích hợp của [, nhưng thường vẫn có một tệp thực thi bên ngoài của tên đó, ví dụ / bin / [.

[[là phiên bản cải tiến mới của nó, là một từ khóa, không phải là một chương trình. Điều này có tác dụng có lợi cho sự dễ sử dụng, như được hiển thị dưới đây. [[được hiểu bởi KornShell và BASH (ví dụ 2.03), nhưng không phải bởi POSIX hoặc BourneShell cũ.

Và kết luận:

Khi nào thì lệnh kiểm tra mới [[nên được sử dụng và khi nào lệnh cũ [? Nếu tính di động đối với BourneShell là một mối quan tâm, nên sử dụng cú pháp cũ. Mặt khác, tập lệnh yêu cầu BASH hoặc KornShell, cú pháp mới linh hoạt hơn nhiều.


18

Dấu ngoặc trong định nghĩa hàm

Dấu ngoặc đơn ()đang được sử dụng trong định nghĩa hàm:

function_name () { command1 ; command2 ; }

Đó là lý do bạn phải thoát dấu ngoặc ngay cả trong các tham số lệnh:

$ echo (
bash: syntax error near unexpected token `newline'

$ echo \(
(

$ echo () { command echo The command echo was redefined. ; }
$ echo anything
The command echo was redefined.

Oh tôi đã thử trên csh. Lỗi của tôi. Khi tôi thử vào bash, nó hoạt động. Tôi không biết lệnh 'lệnh' của bash.
Chân Kim

Làm thế nào tôi có thể hủy xác định lại lệnh echo ()? (không mở lại bash)
Chan Kim

2
@ChanKim : unset -f echo. Xem help unset.
pabouk

0
Truncate the contents of a variable

$ var="abcde"; echo ${var%d*}
abc

Make substitutions similar to sed

$ var="abcde"; echo ${var/de/12}
abc12

Use a default value

$ default="hello"; unset var; echo ${var:-$default}
hello

Khi bạn trả lời một câu hỏi, đừng chỉ nhắm đến "mã", mà còn cố gắng thêm một lời giải thích ...
Mikev
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.