ความแตกต่างระหว่างวงเล็บเหลี่ยมคู่และเดี่ยวในทุบตีคืออะไร?


426

ฉันแค่สงสัยว่าความแตกต่างระหว่างอะไร

[[ $STRING != foo ]]

และ

[ $STRING != foo ]

คือนอกเหนือจากที่หลังเป็นไปตาม posix พบใน sh และอดีตเป็นนามสกุลที่พบในทุบตี


1
ในกรณีที่คุณสงสัยเกี่ยวกับการไม่ใช้วงเล็บเหลี่ยมเช่นในบริบทของifคำสั่งให้ดูที่mywiki.wooledge.org/BashPitfalls#if_.5Bgrep_foo_myfile.5D
Kev

นอกจากนี้จากเอกสารของ Ubuntu: wiki.ubuntu.com/…
radistao

คำตอบ:


309

มีความแตกต่างหลายประการ ในความคิดของฉันสิ่งสำคัญที่สุดสองสามข้อคือ:

  1. [เป็น builtin ใน Bash และกระสุนทันสมัยอื่น ๆ อีกมากมาย builtin [จะคล้ายกับกับความต้องการที่เพิ่มขึ้นของการปิดtest ]builtins [และtestเลียนแบบการทำงาน/bin/[และ/bin/testพร้อมกับข้อ จำกัด ของพวกเขาเพื่อให้สคริปต์จะเข้ากันได้ย้อนหลัง ไฟล์เรียกทำงานดั้งเดิมยังคงมีอยู่ส่วนใหญ่สำหรับการปฏิบัติตาม POSIX และความเข้ากันได้ย้อนหลัง การรันคำสั่งtype [ใน Bash บ่งชี้ว่า[ถูกตีความว่าเป็น builtin โดยค่าเริ่มต้น (หมายเหตุ: which [มองหาไฟล์ที่เรียกทำงานได้บนPATHและเทียบเท่าเท่านั้นtype -p [)
  2. [[ไม่เป็นที่เข้ากันได้ก็จะไม่จำเป็นต้องทำงานกับสิ่งที่/bin/shชี้ไปยัง ดังนั้น[[ตัวเลือก Bash / Zsh / Ksh ที่ทันสมัยกว่า
  3. เนื่องจาก[[สร้างขึ้นในเชลล์และไม่มีข้อกำหนดแบบดั้งเดิมคุณไม่จำเป็นต้องกังวลเกี่ยวกับการแบ่งคำตามตัวแปรIFSเพื่อแยกแยะตัวแปรที่ประเมินเป็นสตริงที่มีช่องว่าง ดังนั้นคุณไม่จำเป็นต้องใส่ตัวแปรในเครื่องหมายคำพูดคู่

ส่วนใหญ่ที่เหลือเป็นเพียงไวยากรณ์ที่ดีกว่า หากต้องการดูความแตกต่างเพิ่มเติมฉันแนะนำลิงก์นี้ไปยังคำตอบที่พบบ่อย: ความแตกต่างระหว่างการทดสอบคืออะไร [และ [[?] . ในความเป็นจริงหากคุณจริงจังเกี่ยวกับการเขียนสคริปต์ทุบตีฉันขอแนะนำให้อ่านwikiทั้งหมดรวมถึงคำถามที่พบบ่อยข้อผิดพลาดและคำแนะนำ ส่วนการทดสอบจากส่วนแนะนำจะอธิบายความแตกต่างเหล่านี้ด้วยและทำไมผู้เขียนคิดว่า[[เป็นตัวเลือกที่ดีกว่าถ้าคุณไม่ต้องกังวลว่าจะพกพาได้ เหตุผลหลักคือ:

  1. คุณไม่ต้องกังวลเกี่ยวกับการอ้างอิงทางด้านซ้ายของการทดสอบเพื่อให้การอ่านเป็นตัวแปรจริง
  2. คุณไม่จำเป็นต้องหลบหนีน้อยกว่าและมากไปกว่า< >กับแบ็กสแลชเพื่อไม่ให้ถูกประเมินว่าเป็นการเปลี่ยนเส้นทางอินพุตซึ่งจริงๆแล้วอาจทำให้บางอย่างยุ่งเหยิงด้วยการเขียนทับไฟล์ สิ่งนี้กลับไป[[เป็นเหมือนเดิมอีกครั้ง ถ้า [(ทดสอบ) เป็นโปรแกรมภายนอกเชลล์จะต้องทำการยกเว้นในวิธีที่มันประเมินค่า<และ>ก็ต่อเมื่อ/bin/testถูกเรียกซึ่งจะไม่สมเหตุสมผลจริงๆ

5
ขอบคุณลิงค์ไปยังคำถามที่พบบ่อยทุบตีคือสิ่งที่ฉันกำลังมองหา (ไม่ทราบเกี่ยวกับหน้านั้นขอบคุณ)
0x89

2
ฉันแก้ไขโพสต์ของคุณด้วยข้อมูลนี้ แต่ [และทดสอบจะถูกดำเนินการเป็น builtins builtins ได้รับการออกแบบมาเพื่อแทนที่ / bin / [และ / bin / test แต่จำเป็นต้องสร้างข้อ จำกัด ของไบนารีอีกด้วย คำสั่ง 'type [' ตรวจสอบว่ามีการใช้ builtin 'ซึ่ง [' ค้นหาเฉพาะไฟล์ปฏิบัติการบน PATH และเทียบเท่ากับ 'type -P ['
klynch

133

ในระยะสั้น:

[คือทุบตีBuiltin

[[]] เป็นคำหลักทุบตี

คำหลัก:คำหลักเป็นเหมือน builtins แต่ความแตกต่างที่สำคัญคือกฎการวิเคราะห์คำพิเศษมีผลกับพวกเขา ตัวอย่างเช่น [คือ bash builtin ในขณะที่ [[คือคำหลัก bash ทั้งสองใช้สำหรับการทดสอบสิ่งของ แต่เนื่องจาก [[เป็นคำหลักมากกว่าแบบบิลด์อินมันจึงได้รับประโยชน์จากกฎการแยกวิเคราะห์พิเศษสองสามข้อซึ่งทำให้ง่ายขึ้นมาก:

  $ [ a < b ]
 -bash: b: No such file or directory
  $ [[ a < b ]]

ตัวอย่างแรกคืนค่าข้อผิดพลาดเนื่องจาก bash พยายามเปลี่ยนเส้นทางไฟล์ b ไปยังคำสั่ง [a] ตัวอย่างที่สองทำในสิ่งที่คุณคาดหวัง อักขระ <ไม่มีความหมายพิเศษของโอเปอเรเตอร์การเปลี่ยนเส้นทางไฟล์อีกต่อไป

ที่มา: http://mywiki.wooledge.org/BashGuide/CommandsAndArguments


3
[เป็นคำสั่งเชลล์ POSIX ไม่จำเป็นต้องสร้างขึ้นมา ]เป็นเพียงอาร์กิวเมนต์ที่คำสั่งนั้นมองหาเพื่อให้ไวยากรณ์มีความสมดุล คำสั่งเป็นคำพ้องสำหรับtestยกเว้นว่าไม่ได้มองหาปิดtest ]
Kaz


81

ความแตกต่างของพฤติกรรม

ความแตกต่างบางประการใน Bash 4.3.11:

  • POSIX vs Bash extension:

  • คำสั่งปกติกับเวทมนตร์

    • [ เป็นเพียงคำสั่งปกติที่มีชื่อแปลก ๆ

      ]เป็นเพียงข้อโต้แย้ง[ที่ป้องกันไม่ให้มีการใช้ข้อโต้แย้งเพิ่มเติม

      จริง ๆ แล้ว Ubuntu 16.04 มีไฟล์ปฏิบัติการที่/usr/bin/[ให้มาโดย coreutils แต่เวอร์ชั่นในตัวของ bash นั้นมีความสำคัญกว่า

      ไม่มีการเปลี่ยนแปลงในวิธีที่ Bash วิเคราะห์คำสั่ง

      โดยเฉพาะอย่างยิ่ง<คือการเปลี่ยนเส้นทาง&&และ||เชื่อมคำสั่งหลาย ๆ คำสั่ง( )สร้าง subshells เว้นแต่จะหลบหนีไป\และการขยายคำจะเกิดขึ้นตามปกติ

    • [[ X ]]เป็นโครงสร้างเดียวที่ทำให้การXแยกวิเคราะห์อย่างน่าอัศจรรย์ <, &&, ||และ()ได้รับการปฏิบัติเป็นพิเศษและกฎการแยกคำที่แตกต่างกัน

      นอกจากนี้ยังมีความแตกต่างต่อไปเหมือนและ==~

      ใน Bashese: [เป็นคำสั่งในตัวและ[[เป็นคำหลัก: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && และ ||

    • [[ a = a && b = b ]]: จริงตรรกะและ
    • [ a = a && b = b ]: ข้อผิดพลาดทางไวยากรณ์&&แยกวิเคราะห์เป็นตัวคั่นคำสั่ง ANDcmd1 && cmd2
    • [ a = a -a b = b ]: เทียบเท่า แต่เลิกใช้แล้วโดยPOSIX³
    • [ a = a ] && [ b = b ]: POSIX และเทียบเท่าที่เชื่อถือได้
  • (

    • [[ (a = a || a = b) && a = b ]]: false
    • [ ( a = a ) ]: ข้อผิดพลาดทางไวยากรณ์()ถูกตีความว่าเป็น subshell
    • [ \( a = a -o a = b \) -a a = b ]: เทียบเท่า แต่()เลิกใช้แล้วโดย POSIX
    • { [ a = a ] || [ a = b ]; } && [ a = b ]POSIX เทียบเท่า5
  • การแยกคำและการสร้างชื่อไฟล์ตามการขยาย (แยก + glob)

    • x='a b'; [[ $x = 'a b' ]]: จริงไม่จำเป็นต้องใส่เครื่องหมายคำพูด
    • x='a b'; [ $x = 'a b' ]: ข้อผิดพลาดทางไวยากรณ์ขยายเป็น [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: ข้อผิดพลาดทางไวยากรณ์หากมีมากกว่าหนึ่งไฟล์ในไดเรกทอรีปัจจุบัน
    • x='a b'; [ "$x" = 'a b' ]: เทียบเท่า POSIX
  • =

    • [[ ab = a? ]]: จริงเพราะมันจับคู่รูปแบบ ( * ? [เป็นเวทย์มนตร์) ห้าม glob ขยายไฟล์ในไดเรกทอรีปัจจุบัน
    • [ ab = a? ]: a?glob ขยายตัว ดังนั้นอาจเป็นจริงหรือเท็จขึ้นอยู่กับไฟล์ในไดเรกทอรีปัจจุบัน
    • [ ab = a\? ]: เท็จไม่ใช่การขยาย glob
    • =และ==เหมือนกันทั้งใน[และ[[, แต่==เป็นส่วนขยายของ Bash
    • case ab in (a?) echo match; esac: เทียบเท่า POSIX
    • [[ ab =~ 'ab?' ]]: false 4 , สูญเสียเวทย์ด้วย''
    • [[ ab? =~ 'ab?' ]]: จริง
  • =~

    • [[ ab =~ ab? ]]: จริง POSIX ขยายการจับคู่การแสดงออกปกติ?ไม่ได้ขยาย
    • [ a =~ a ]: ข้อผิดพลาดทางไวยากรณ์ ไม่เทียบเท่าทุบตี
    • printf 'ab\n' | grep -Eq 'ab?': เทียบเท่า POSIX (ข้อมูลบรรทัดเดียวเท่านั้น)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': เทียบเท่า POSIX

คำแนะนำ : []มักจะใช้

มี POSIX ที่เทียบเท่าสำหรับการ[[ ]]สร้างทุกครั้งที่ฉันเห็น

ถ้าคุณใช้[[ ]]คุณ:

  • สูญเสียการพกพา
  • บังคับให้ผู้อ่านเรียนรู้ความซับซ้อนของส่วนขยาย bash อื่น [เป็นเพียงคำสั่งปกติที่มีชื่อแปลก ๆ ไม่มีความหมายพิเศษเกี่ยวข้อง

¹แรงบันดาลใจจากสิ่ง[[...]]ก่อสร้างที่เทียบเท่าใน Korn เชลล์

² แต่ล้มเหลวสำหรับบางค่าของaหรือb(เช่น+หรือindex) และทำการเปรียบเทียบตัวเลขถ้าaและbดูเหมือนเลขจำนวนเต็มฐานสิบ expr "x$a" '<' "x$b"ทำงานได้ทั้งสองอย่าง

³และยังล้มเหลวสำหรับค่าของบางส่วนaหรือbเหมือนหรือ!(

4ใน bash 3.2 ขึ้นไปและไม่สามารถเปิดใช้งานความเข้ากันได้กับ bash 3.1 (เช่นเดียวกับBASH_COMPAT=3.1)

5แม้ว่าการจัดกลุ่ม (ที่นี่พร้อมกับ{...;}กลุ่มคำสั่งแทนที่จะเป็นกลุ่ม(...)ย่อยที่ไม่จำเป็น) ไม่จำเป็นเนื่องจากตัวดำเนินการ||และ&&เชลล์ (ตรงข้ามกับตัวดำเนินการ||และ&& [[...]]ตัวดำเนินการหรือ-o/ -a [ตัวดำเนินการ/ ) มีลำดับความสำคัญเท่ากัน ดังนั้น[ a = a ] || [ a = b ] && [ a = b ]จะเทียบเท่า


วิธีการใช้งานprintf 'ab' | grep -Eq 'ab?'ภายในif [ … ]?
meeDamian

1
if ( printf 'ab' | grep -Eq 'a' ); then echo 'a'; fi@meeDamian เป็นคำสั่งที่เหมือน[] อาจไม่จำเป็นในคำสั่งที่ผมไม่แน่ใจว่า: ฉันเพิ่มมันเพราะขึ้นอยู่กับวิธีการแยกวิเคราะห์สิ่งทุบตี หากไม่มีฉันแน่ใจว่าคุณสามารถเขียนได้ grep()||if cmd arg arg; then
Ciro Santilli 新疆改造中心法轮功六四事件

1
@meeDamian ใช่ไม่จำเป็นต้อง()ดู: stackoverflow.com/questions/8965509/ …
Ciro Santilli 新疆改造中心中心法轮功六四事件

1
รายการที่ดี! ดูเพิ่มเติมที่: wiki.ubuntu.com/…
radistao

5

Bracket เดียวคือ[]เป็น POSIX เปลือกสอดคล้องกับการแนบนิพจน์เงื่อนไข

ดับเบิลวงเล็บคือ[[]]เป็นเพิ่มขึ้น (หรือส่วนขยาย) ของรุ่น POSIX มาตรฐานนี้ได้รับการสนับสนุนโดยการทุบตีและเปลือกหอยอื่น ๆ (zsh, ksh)

ในทุบตีสำหรับการเปรียบเทียบตัวเลขที่เราใช้eq, ne, ltและgtมีวงเล็บคู่สำหรับการเปรียบเทียบเราสามารถใช้==, !=, <,และ>ตัวอักษร

  • [เป็นคำพ้องสำหรับคำสั่งทดสอบ แม้ว่ามันจะถูกสร้างไว้ในเชลล์มันจะสร้างกระบวนการใหม่
  • [[ เป็นรุ่นที่ปรับปรุงใหม่ของมันซึ่งเป็นคำหลักไม่ใช่โปรแกรม

ตัวอย่างเช่น:

[ var1 lt var2] #works
[ var1 < var2] #error: var2 No such file or directory 
[ var1 \< var2] #works with escape
[[ var1 < var2]] #works

4

จากการอ่านอย่างรวดเร็วของส่วนที่เกี่ยวข้องของ manpage ความแตกต่างหลักปรากฏว่าตัวดำเนินการ==และ!=ตัวจับคู่กับรูปแบบไม่ใช่สตริงตัวอักษรและยังมีตัว=~ดำเนินการเปรียบเทียบ regex

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