วิธีการใช้วงเล็บคู่หรือเดี่ยววงเล็บวงเล็บปีกกา


658

ฉันสับสนโดยการใช้วงเล็บวงเล็บวงเล็บปีกกาใน Bash เช่นเดียวกับความแตกต่างระหว่างรูปแบบสองหรือเดี่ยวของพวกเขา มีคำอธิบายที่ชัดเจนหรือไม่?

คำตอบ:


606

ใน Bash testและ[เป็น shell builtins

เครื่องหมายวงเล็บเหลี่ยมคู่ซึ่งเป็นคีย์เวิร์ดเชลล์เปิดใช้งานฟังก์ชันเพิ่มเติม ตัวอย่างเช่นคุณสามารถใช้&&และ||แทนที่-aและ-oและมีตัวดำเนินการจับคู่นิพจน์ทั่วไป=~และมีผู้ประกอบการแสดงออกของการจับคู่ปกติ

นอกจากนี้ในการทดสอบอย่างง่ายวงเล็บเหลี่ยมสองชั้นดูเหมือนจะประเมินได้เร็วกว่าแบบเดี่ยวมาก

$ 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

เครื่องมือจัดฟันนอกเหนือจากการกำหนดชื่อตัวแปรให้ใช้สำหรับการขยายพารามิเตอร์เพื่อให้คุณสามารถทำสิ่งต่าง ๆ เช่น:

  • ตัดเนื้อหาของตัวแปรออก

    $ var="abcde"; echo ${var%d*}
    abc
  • ทำให้การทดแทนคล้ายกับ sed

    $ var="abcde"; echo ${var/de/12}
    abc12
  • ใช้ค่าเริ่มต้น

    $ default="hello"; unset var; echo ${var:-$default}
    hello
  • และอีกมากมาย

นอกจากนี้การขยายรั้งจะสร้างรายการของสตริงซึ่งโดยทั่วไปจะวนซ้ำในลูป:

$ 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

โปรดทราบว่าคุณลักษณะนำหน้าศูนย์และการเพิ่มขึ้นไม่พร้อมใช้งานก่อน Bash 4

ขอบคุณ gboffi สำหรับเตือนฉันเกี่ยวกับการขยายรั้ง

เครื่องหมายวงเล็บคู่ใช้สำหรับการดำเนินการทางคณิตศาสตร์ :

((a++))

((meaning = 42))

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

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

และพวกเขาทำให้คุณสามารถละเว้นเครื่องหมายดอลลาร์เกี่ยวกับตัวแปรจำนวนเต็มและอาร์เรย์และรวมถึงการเว้นวรรครอบตัวดำเนินการเพื่อให้สามารถอ่านได้

วงเล็บเดียวยังใช้สำหรับดัชนีอาร์เรย์ :

array[4]="hello"

element=${array[index]}

ต้องใช้เครื่องหมายปีกกาสำหรับการอ้างอิงอาเรย์ (ส่วนใหญ่ / ทั้งหมด?) ทางด้านขวามือ

ความคิดเห็นของ ephemientทำให้ฉันนึกถึงว่ามีการใช้วงเล็บใน subshells ด้วยเช่นกัน และพวกเขาจะใช้ในการสร้างอาร์เรย์

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

8
คำเตือน: ฟังก์ชั่นนั้นเป็น fork fork ห้ามใช้งาน ดู: en.wikipedia.org/wiki/Fork_bomb
หยุดชั่วคราวจนกว่าจะมีประกาศเพิ่มเติม

3
:มันเป็นเพียงระเบิดส้อมถ้าคุณเรียกมันเพิ่มเติมด้วย
ephemient

7
เพื่อความสมบูรณ์ฉันเพิ่งเจอสิ่งนี้ในสคริปต์เก่า: $[expression]; นี่คือไวยากรณ์นิพจน์ทางคณิตศาสตร์เก่าที่คัดค้านสำหรับไวยากรณ์ที่ใหม่กว่าและต้องการ:$((expression))
michael

2
@DennisWilliamson การใช้วงเล็บปีกกาอีกอันในbashการสร้างลำดับดังที่กล่าวไว้ด้านล่าง ( stackoverflow.com/a/8552128/2749397 ) ตามที่ฉันต้องการจะแสดงความคิดเห็นเล็กน้อยคุณลักษณะนี้ (ตามที่คุณไม่ได้พูดถึงมัน ;-) ฉัน ' m ใช้เสรีภาพในการใช้คำตอบที่ได้รับการโหวตมากที่สุดในฐานะพาหนะ ... ตัวอย่างสองตัวอย่างของตัวอักษรตามลำดับ: echo {01..12}-> 01 02 03 04 05 06 07 08 09 10 11 12(บันทึกศูนย์เริ่มต้น); ->echo {C..Q} C D E F G H I J K L M N O P Qการใช้งานหลักคือลูปเช่น for cnt in {01..12} ; do ... ${cnt} ... ; done
gboffi

1
@gboffi: คุณลักษณะการเติมเต็มศูนย์พร้อมใช้งานใน Bash 4 นอกจากนี้ใน Bash 4 คุณสามารถระบุการเพิ่มขึ้นตามลำดับ: echo {01..12..2}-> "01 03 05 07 09 11" ขอบคุณสำหรับการเตือนเกี่ยวกับลำดับ ฉันจะเพิ่มเข้าไปในคำตอบของฉัน
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

336
  1. เครื่องหมายวงเล็บเดี่ยว ( [) มักจะเรียกชื่อโปรแกรม[จริง man testหรือman [สำหรับข้อมูลเพิ่มเติม ตัวอย่าง:

    $ VARIABLE=abcdef
    $ if [ $VARIABLE == abcdef ] ; then echo yes ; else echo no ; fi
    yes
  2. วงเล็บคู่ ( [[) ทำสิ่งเดียวกัน (โดยทั่วไป) เป็นวงเล็บเดียว แต่เป็นตัวทุบตีในตัว

    $ VARIABLE=abcdef
    $ if [[ $VARIABLE == 123456 ]] ; then echo yes ; else echo no ; fi
    no
  3. วงเล็บ ( ()) ถูกใช้เพื่อสร้าง subshell ตัวอย่างเช่น:

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

    อย่างที่คุณเห็น subshell อนุญาตให้คุณทำการดำเนินการโดยไม่มีผลกระทบต่อสภาพแวดล้อมของเชลล์ปัจจุบัน

  4. (a) เครื่องมือจัดฟัน ( {}) ใช้เพื่อระบุตัวแปรอย่างไม่น่าสงสัย ตัวอย่าง:

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

    (b) การจัดฟันใช้เพื่อดำเนินการตามลำดับของคำสั่งในบริบทเชลล์ปัจจุบันเช่น

    $ { 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

มีความแตกต่างทางวากยสัมพันธ์ที่ลึกซึ้งด้วย( )แม้ว่า (ดูการอ้างอิงทุบตี ); หลักอัฒภาค;หลังจากที่คำสั่งสุดท้ายที่อยู่ในวงเล็บคือต้องและวงเล็บ{, } ต้องถูกล้อมรอบด้วยช่องว่าง


26
ดี[เป็นจริงใน builtin ทุบตี แต่มันควรจะทำหน้าที่เหมือน/bin/[เมื่อเทียบกับ[[ในตัว [[มีคุณสมบัติที่แตกต่างกันเช่นการทำงานแบบลอจิคัลมากกว่าและบทบาทที่ต่างกัน นอกจากนี้: วงเล็บเดียวยังใช้สำหรับอาร์เรย์การแทนกระบวนการและการขยายเพิ่มเติม เครื่องหมายวงเล็บคู่ใช้สำหรับการคำนวณ วงเล็บปีกกา{}จะใช้สำหรับการจัดกลุ่มคำสั่งหรือหลากหลายประเภทของการขยายพารามิเตอร์หรือการขยายรั้งหรือการขยายลำดับ ฉันแน่ใจว่าฉันพลาดการใช้งานอื่นด้วย
ephemient

4
double-equals ใน expression if [ $VARIABLE == abcdef ]คือ bashism ที่แม้ว่ามันจะใช้ได้ดี แต่ก็ควรหลีกเลี่ยง ใช้ bash ( if [[ ...==...]]) อย่างชัดเจนหรือทำให้ชัดเจนว่าคุณใช้เงื่อนไขแบบดั้งเดิมมากขึ้น ( if [ "$VARIABLE" = "abcdef" ]) เนื้อหาควรเริ่มจากสคริปต์ที่เรียบง่ายและพกพาได้มากที่สุดจนกว่าพวกเขาจะต้องการคุณลักษณะเฉพาะสำหรับการทุบตี (ด้วยเหตุผลใดก็ตาม) แต่ไม่ว่าในกรณีใดเจตนาจะชัดเจน "=" และ "==" และ "[[" และ "[" ทำงานแตกต่างกันและการใช้งานควรสอดคล้องกัน
ไมเคิล

3
@michael_n: +1 สำหรับคำพูดนี้ ในบันทึกด้านข้างฉันรักการเขียนสคริปต์ แต่ฉันคิดว่ามันค่อนข้างงุ่มง่ามที่วิธีพกพาคือการทดสอบผ่าน[ "$var" = ".."]แทนที่จะเป็น==ในขณะที่ C จะกำหนดแทนการทดสอบ (และค่อนข้างเป็นสาเหตุของข้อบกพร่อง) ... ทำไมไม่ 'T testใช้==แทน=? มีใครรู้บ้าง
Olivier Dulac

นอกจากนี้ที่นี่เป็นสิ่งที่ตลก(อย่างน้อยใน Kubuntu)คำสั่ง/usr/bin/[ไม่ใช่ symlink ไป/usr/bin/testและอื่น ๆ : โปรแกรมเหล่านี้มีขนาดแตกต่างกันเล็กน้อย!
Hi-Angel

นอกจากนี้: วงเล็บปิดเดี่ยว)เป็นส่วนหนึ่งของcaseไวยากรณ์คำสั่งเพื่อสิ้นสุดบรรทัดเคส มันไม่มีวงเล็บเปิด นี่เป็นการขว้างฉันครั้งแรกที่ฉันเห็นมัน
Agustín Amenabar

302

วงเล็บ

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

วงเล็บปีกกา

${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

วงเล็บ

( 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 

วงเล็บคู่

(( 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 คุณช่วยอธิบายหน่อยได้ไหมว่า $ (varname) ตรงไหน? ในโครงการ Apple Xcode ฉันสามารถระบุพา ธ ไฟล์เป็นสคริปต์อินพุต / เอาต์พุต ถ้าฉันระบุ $ SRC_ROOT / myFile.txt หรือ $ {SRC_ROOT} /myFile.txt (SRC_ROOT var ถูกส่งออกโดยระบบบิลด์) - ไม่ทำงาน ทำงานได้เพียง $ (SRC_ROOT) /myFile.txt อะไรคือเหตุผล? ชัดเจนชื่อ var ไม่ใช่คำสั่ง?
Motti Shneor

1
@MottiShneor ในกรณีของคุณ$(varname)ไม่เกี่ยวข้องกับ bash syntax มันเป็นส่วนหนึ่งของไวยากรณ์ Makefile
Sasha

ไม่เช่นนั้น - Xcode ไม่ได้สร้างโดยใช้ makefile และตัวแปรนั้นเป็นตัวแปรสภาพแวดล้อม กระบวนการสร้างระบบ Xcode ที่เป็นกรรมสิทธิ์จะอ่านค่าของตัวแปรสภาพแวดล้อมที่กำหนดไว้ล่วงหน้าเหล่านี้ Custom-build-steps เป็นเพียงเชลล์สคริปต์ปกติ (bash หรืออื่น ๆ ) และมีสิทธิ์เข้าถึง vars เดียวกัน
Motti Shneor

@MottiShneor ตกลงให้สินค้าของ: ส่วนใหญ่อาจจะเป็นส่วนหนึ่งของไวยากรณ์ xcconfig อย่างไรก็ตาม$(varname)ไม่มีความเกี่ยวข้องกับ bash syntax ในกรณีของคุณ
Sasha

คุณไม่ต้องพูดถึงความแตกต่างระหว่างโครงสร้างการทดสอบและโครงสร้างการทดสอบแบบขยายคืออะไร
Nikos

23

ฉันแค่อยากจะเพิ่มสิ่งเหล่านี้จาก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
ใจที่echo ${#ARRAY}แสดงสามเพราะองค์ประกอบแรกของARRAYประกอบด้วยสามอักขระไม่ใช่เพราะมันมีสามองค์ประกอบ! echo ${#ARRAY[@]}หากต้องการพิมพ์จำนวนการใช้องค์ประกอบ
TrueY

@zeal ${TEST:-test}เท่ากับ$TESTว่ามีตัวแปรTESTอยู่มิฉะนั้นจะส่งคืนสตริง "test" มีอีกเวอร์ชั่นหนึ่งที่ทำอะไรได้มากกว่า: ${TEST:=test}--- ซึ่งเท่ากับ$TESTถ้ามีการทดสอบอยู่ แต่เมื่อใดก็ตามที่ไม่มีก็จะสร้างตัวแปรTESTและกำหนดค่า "ทดสอบ" และยังกลายเป็นค่าของนิพจน์ทั้งหมด
รักความน่าจะเป็น

18

ความแตกต่างระหว่างการทดสอบ , [และ[[มีการอธิบายในรายละเอียดมากในBashFAQ

หากต้องการตัดเรื่องสั้นให้สั้น: ทดสอบใช้ไวยากรณ์แบบพกพาเก่าของคำสั่ง ในเชลล์เกือบทั้งหมด (เชลล์ Bourne ที่เก่าที่สุดเป็นข้อยกเว้น), [เป็นคำพ้องสำหรับการทดสอบ (แต่ต้องการอาร์กิวเมนต์สุดท้ายของ) แม้ว่าเชลล์ที่ทันสมัยทั้งหมดมีการใช้งานในตัวของ [แต่ก็ยังคงมีความสามารถในการเรียกใช้งานภายนอกของชื่อนั้นได้เช่น / bin / [

[[เป็นรุ่นที่ปรับปรุงใหม่ของมันซึ่งเป็นคำหลักไม่ใช่โปรแกรม สิ่งนี้มีผลประโยชน์ในการใช้งานง่ายดังแสดงด้านล่าง [[เข้าใจโดย KornShell และ BASH (เช่น 2.03) แต่ไม่ใช่ POSIX หรือ BourneShell ที่เก่ากว่า

และข้อสรุป:

คำสั่งทดสอบใหม่ [ควรจะใช้เมื่อไรและเมื่อไหร่? หากการพกพาไปยัง BourneShell เป็นเรื่องที่น่ากังวลควรใช้ไวยากรณ์เก่า หากในอีกทางหนึ่งสคริปต์ต้องการ BASH หรือ KornShell ไวยากรณ์ใหม่นั้นมีความยืดหยุ่นมากกว่า


18

วงเล็บในคำจำกัดความของฟังก์ชัน

()กำลังใช้ วงเล็บในคำจำกัดความของฟังก์ชัน:

function_name () { command1 ; command2 ; }

นั่นคือเหตุผลที่คุณต้องหลีกเลี่ยงวงเล็บในพารามิเตอร์คำสั่ง:

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

$ echo \(
(

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

โอ้ฉันลองใน csh ความผิดฉันเอง. เมื่อฉันลองทุบตีมันก็ใช้ได้ดี ฉันไม่ทราบคำสั่ง 'command' ของ bash
Chan Kim

ฉันจะยกเลิกการกำหนดคำสั่ง echo () ใหม่ได้อย่างไร? (โดยไม่ต้องเปิด bash ใหม่)
Chan Kim

2
unset -f echo@ChanKim: 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

เมื่อคุณตอบคำถามอย่าเพิ่งมุ่ง "รหัส" แต่ลองเพิ่มคำอธิบาย ...
Mikev
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.