ฉันแค่สงสัยว่าความแตกต่างระหว่างอะไร
[[ $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