ฉันแค่สงสัยว่าความแตกต่างระหว่างอะไร
[[ $STRING != foo ]]
และ
[ $STRING != foo ]
คือนอกเหนือจากที่หลังเป็นไปตาม posix พบใน sh และอดีตเป็นนามสกุลที่พบในทุบตี
ฉันแค่สงสัยว่าความแตกต่างระหว่างอะไร
[[ $STRING != foo ]]
และ
[ $STRING != foo ]
คือนอกเหนือจากที่หลังเป็นไปตาม posix พบใน sh และอดีตเป็นนามสกุลที่พบในทุบตี
คำตอบ:
มีความแตกต่างหลายประการ ในความคิดของฉันสิ่งสำคัญที่สุดสองสามข้อคือ:
[เป็น builtin ใน Bash และกระสุนทันสมัยอื่น ๆ อีกมากมาย builtin [จะคล้ายกับกับความต้องการที่เพิ่มขึ้นของการปิดtest ]builtins [และtestเลียนแบบการทำงาน/bin/[และ/bin/testพร้อมกับข้อ จำกัด ของพวกเขาเพื่อให้สคริปต์จะเข้ากันได้ย้อนหลัง ไฟล์เรียกทำงานดั้งเดิมยังคงมีอยู่ส่วนใหญ่สำหรับการปฏิบัติตาม POSIX และความเข้ากันได้ย้อนหลัง การรันคำสั่งtype [ใน Bash บ่งชี้ว่า[ถูกตีความว่าเป็น builtin โดยค่าเริ่มต้น (หมายเหตุ: which [มองหาไฟล์ที่เรียกทำงานได้บนPATHและเทียบเท่าเท่านั้นtype -p [)[[ไม่เป็นที่เข้ากันได้ก็จะไม่จำเป็นต้องทำงานกับสิ่งที่/bin/shชี้ไปยัง ดังนั้น[[ตัวเลือก Bash / Zsh / Ksh ที่ทันสมัยกว่า[[สร้างขึ้นในเชลล์และไม่มีข้อกำหนดแบบดั้งเดิมคุณไม่จำเป็นต้องกังวลเกี่ยวกับการแบ่งคำตามตัวแปรIFSเพื่อแยกแยะตัวแปรที่ประเมินเป็นสตริงที่มีช่องว่าง ดังนั้นคุณไม่จำเป็นต้องใส่ตัวแปรในเครื่องหมายคำพูดคู่ส่วนใหญ่ที่เหลือเป็นเพียงไวยากรณ์ที่ดีกว่า หากต้องการดูความแตกต่างเพิ่มเติมฉันแนะนำลิงก์นี้ไปยังคำตอบที่พบบ่อย: ความแตกต่างระหว่างการทดสอบคืออะไร [และ [[?] . ในความเป็นจริงหากคุณจริงจังเกี่ยวกับการเขียนสคริปต์ทุบตีฉันขอแนะนำให้อ่านwikiทั้งหมดรวมถึงคำถามที่พบบ่อยข้อผิดพลาดและคำแนะนำ ส่วนการทดสอบจากส่วนแนะนำจะอธิบายความแตกต่างเหล่านี้ด้วยและทำไมผู้เขียนคิดว่า[[เป็นตัวเลือกที่ดีกว่าถ้าคุณไม่ต้องกังวลว่าจะพกพาได้ เหตุผลหลักคือ:
< >กับแบ็กสแลชเพื่อไม่ให้ถูกประเมินว่าเป็นการเปลี่ยนเส้นทางอินพุตซึ่งจริงๆแล้วอาจทำให้บางอย่างยุ่งเหยิงด้วยการเขียนทับไฟล์ สิ่งนี้กลับไป[[เป็นเหมือนเดิมอีกครั้ง ถ้า [(ทดสอบ) เป็นโปรแกรมภายนอกเชลล์จะต้องทำการยกเว้นในวิธีที่มันประเมินค่า<และ>ก็ต่อเมื่อ/bin/testถูกเรียกซึ่งจะไม่สมเหตุสมผลจริงๆในระยะสั้น:
[คือทุบตี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
[เป็นคำสั่งเชลล์ POSIX ไม่จำเป็นต้องสร้างขึ้นมา ]เป็นเพียงอาร์กิวเมนต์ที่คำสั่งนั้นมองหาเพื่อให้ไวยากรณ์มีความสมดุล คำสั่งเป็นคำพ้องสำหรับtestยกเว้นว่าไม่ได้มองหาปิดtest ]
ความแตกต่างของพฤติกรรม
ความแตกต่างบางประการใน Bash 4.3.11:
POSIX vs Bash extension:
[ POSIX คือ[[เป็นส่วนขยายของBash¹ที่: https://www.gnu.org/software/bash/manual/bash.html#Conditional-Constructsคำสั่งปกติกับเวทมนตร์
[ เป็นเพียงคำสั่งปกติที่มีชื่อแปลก ๆ
]เป็นเพียงข้อโต้แย้ง[ที่ป้องกันไม่ให้มีการใช้ข้อโต้แย้งเพิ่มเติม
จริง ๆ แล้ว 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 < b ]]: การเปรียบเทียบพจนานุกรม[ a \< b ]: เหมือนด้านบน \จำเป็นหรือไม่ก็เปลี่ยนเส้นทางเหมือนสำหรับคำสั่งอื่น ๆ นามสกุลทุบตีexpr a \< b > /dev/null: POSIX เทียบเท่า²ดู: https://stackoverflow.com/questions/21294867/how-to-test-strings-for-lexicographic-less-than-or-equal-in-bash/52707989#52707989&& และ ||
[[ 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=และ==เหมือนกันทั้งใน[และ[[, แต่==เป็นส่วนขยายของ Bashcase 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 ที่เทียบเท่าสำหรับการ[[ ]]สร้างทุกครั้งที่ฉันเห็น
ถ้าคุณใช้[[ ]]คุณ:
[เป็นเพียงคำสั่งปกติที่มีชื่อแปลก ๆ ไม่มีความหมายพิเศษเกี่ยวข้อง¹แรงบันดาลใจจากสิ่ง[[...]]ก่อสร้างที่เทียบเท่าใน 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 [ … ]?
if ( printf 'ab' | grep -Eq 'a' ); then echo 'a'; fi@meeDamian เป็นคำสั่งที่เหมือน[] อาจไม่จำเป็นในคำสั่งที่ผมไม่แน่ใจว่า: ฉันเพิ่มมันเพราะขึ้นอยู่กับวิธีการแยกวิเคราะห์สิ่งทุบตี หากไม่มีฉันแน่ใจว่าคุณสามารถเขียนได้ grep()||if cmd arg arg; then
()ดู: stackoverflow.com/questions/8965509/ …
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
จากการอ่านอย่างรวดเร็วของส่วนที่เกี่ยวข้องของ manpage ความแตกต่างหลักปรากฏว่าตัวดำเนินการ==และ!=ตัวจับคู่กับรูปแบบไม่ใช่สตริงตัวอักษรและยังมีตัว=~ดำเนินการเปรียบเทียบ regex
ifคำสั่งให้ดูที่mywiki.wooledge.org/BashPitfalls#if_.5Bgrep_foo_myfile.5D