อะไรคือความแตกต่างระหว่าง [[$ a == z *]] และ [$ a == z *]


35

มีความแตกต่างระหว่างสองสิ่งนี้หรือไม่

[[ $a == z* ]]

และ

[ $a == z* ] 

ฉันสามารถมีตัวอย่างที่พวกเขาจะมีผลลัพธ์ที่แตกต่างกันหรือไม่

นอกจากนี้การทำงานของ[[ ]]แตกต่างกัน[ ]อย่างไร

คำตอบ:


41

ความแตกต่างระหว่าง[[ … ]]และ[ … ]ถูกปกคลุมส่วนใหญ่ในการใช้วงเล็บเดี่ยวหรือเตียงคู่ - ทุบตี Crucially [[ … ]]เป็นไวยากรณ์พิเศษในขณะที่[เป็นชื่อที่ดูตลกสำหรับคำสั่ง [[ … ]]มีกฎไวยากรณ์พิเศษสำหรับสิ่งที่อยู่ภายใน[ … ]ไม่ได้

ด้วยรอยย่นเพิ่มเติมของ wildcard นี่คือวิธีการ[[ $a == z* ]]ประเมิน:

  1. แจงคำสั่ง: นี่คือการสร้างเงื่อนไขรอบนิพจน์เงื่อนไข[[ … ]]$a == z*
  2. แยกนิพจน์เงื่อนไข: นี้เป็น==ผู้ประกอบการไบนารีกับตัวถูกดำเนินและ$az*
  3. aขยายตัวถูกดำเนินการครั้งแรกในค่าของตัวแปร
  4. ประเมิน==ผู้ปฏิบัติงาน: ทดสอบว่าค่าของตัวแปรaตรงกับรูปแบบz*หรือไม่
  5. ประเมินนิพจน์เงื่อนไข: ผลลัพธ์คือผลลัพธ์ของตัวดำเนินการตามเงื่อนไข
  6. ขณะนี้คำสั่งถูกประเมินสถานะเป็น 0 ถ้านิพจน์เงื่อนไขเป็นจริงและ 1 ถ้าเป็นเท็จ

นี่คือวิธีการ[ $a == z* ]ประเมิน:

  1. แยกคำสั่ง: นี่เป็น[คำสั่งที่มีการขัดแย้งที่เกิดขึ้นจากการประเมินคำ$a, ==, ,z*]
  2. ขยายเข้าไปในค่าของตัวแปร$aa
  3. ดำเนินการแยกคำและสร้างชื่อไฟล์บนพารามิเตอร์ของคำสั่ง
    • ตัวอย่างเช่นถ้าค่าของการaเป็นสตริง 6 ตัวfoo b*(ที่ได้รับโดยเช่นa='foo b*') และรายชื่อของไฟล์ในไดเรกทอรีปัจจุบันเป็น (คนbar, baz, qux, zim, zum) แล้วผลของการขยายตัวที่เป็นรายการต่อไปนี้คำ: [, foo, bar, baz, ==, zim, ,zum]
  4. รันคำสั่ง[ด้วยพารามิเตอร์ที่ได้รับในขั้นตอนก่อนหน้า
    • ด้วยค่าตัวอย่างด้านบน[คำสั่งบ่นถึงข้อผิดพลาดทางไวยากรณ์และส่งคืนสถานะ 2

หมายเหตุ: ใน[[ $a == z* ]]ขั้นตอนที่ 3 ค่าของaจะไม่ได้รับการแยกคำและการสร้างชื่อไฟล์เนื่องจากอยู่ในบริบทที่คาดว่าจะมีคำเดียว (อาร์กิวเมนต์ซ้ายมือของโอเปอเรเตอร์ที่มีเงื่อนไข==) ในกรณีส่วนใหญ่หากคำเดียวทำให้รู้สึกถึงตำแหน่งนั้นการขยายตัวของตัวแปรจะทำงานเหมือนคำพูดในเครื่องหมายคำพูดคู่ อย่างไรก็ตามมีข้อยกเว้นสำหรับกฎนั้น: ใน[[ abc == $a ]]หากค่าของaประกอบด้วยอักขระตัวแทนนั้นaจะถูกจับคู่กับรูปแบบสัญลักษณ์แทน ตัวอย่างเช่นหากค่าของaเป็นa*แล้ว[[ abc == $a ]]จะเป็นจริง (เพราะอักขระตัวแทน*มาจากการขยายตัวของการ$aจับคู่ที่ไม่มีเงื่อนไข*) ในขณะที่[[ abc == "$a" ]]เป็นเท็จ (เพราะตัวอักษรธรรมดา*มาจากการขยายที่ยกมาของ$aไม่ตรงกับbc) ภายใน[[ … ]], ราคาคู่ไม่ได้สร้างความแตกต่างยกเว้นทางด้านขวามือของผู้ประกอบการจับคู่สาย ( =, ==, !=และ=~)


39

[เป็นนามแฝงสำหรับtestคำสั่ง Unix เวอร์ชัน 6 มีifคำสั่ง แต่เวอร์ชัน 7 (1979) มาพร้อมกับBourne shellใหม่ที่มีโครงสร้างการเขียนโปรแกรมไม่กี่รายการรวมถึงโครงสร้าง if-then-else-elif-fi และ Unix เวอร์ชัน 7 ได้เพิ่มtestคำสั่งที่ดำเนินการเกือบทั้งหมด "การทดสอบ" ที่ดำเนินการโดยifคำสั่งในเวอร์ชันที่เก่ากว่า

[ถูกสร้างขึ้นมาเพื่อนามแฝงtestและทั้งสองได้ทำที่สร้างขึ้นในเปลือกในระบบยูนิกซ์ที่สาม (1981) แม้ว่ามันควรจะสังเกตว่าตัวแปร Unix บางตัวไม่ได้มี[คำสั่งจนกระทั่งมากหลังจากนั้น ( จนถึงต้นยุค 2000 ในshBSD บางอันซึ่งมีพื้นฐานมาจากเชลล์ Almquist ( บิวอินtestได้รวมอยู่ในashแหล่งของมันเสมอ) BSD มันถูกปิดใช้งานในขั้นต้น))

โปรดทราบว่าtestaka [เป็นคำสั่งที่จะทำ "ทดสอบ" =ที่มีการกำหนดว่าคำสั่งที่ไม่เป็นจึงมีเหตุผลที่จะกระจ่างระหว่างผู้ประกอบการที่ได้รับมอบหมายและความเท่าเทียมกันไม่ให้ผู้ประกอบการเท่าเทียมกันคือ ==ได้รับการสนับสนุนโดยการนำไปใช้งานล่าสุดของ[(และเป็นเพียงนามแฝง=)

เนื่องจาก[ไม่มีอะไรมากกว่าคำสั่งจึงแยกวิเคราะห์แบบเดียวกับคำสั่งอื่น ๆ โดยเชลล์

โดยเฉพาะอย่างยิ่งในตัวอย่างของคุณ$aเนื่องจากไม่มีการอ้างอิงจะถูกแบ่งออกเป็นหลายคำตามกฎปกติของการแยกคำและแต่ละคำจะได้รับการสร้างชื่อไฟล์หรือที่รู้จักกันในชื่อ globbing เพื่อให้ได้คำที่มากขึ้น อาร์กิวเมนต์แยกจาก[คำสั่ง

ในทำนองเดียวกันจะขยายไปยังรายการของชื่อไฟล์ในไดเรกทอรีปัจจุบันที่เริ่มต้นด้วยz*z

ดังนั้นสำหรับตัวอย่างเช่นถ้า$aเป็นb* = xและมีz1, z2, b1และb2ไฟล์ในไดเรกทอรีปัจจุบันที่[คำสั่งจะได้รับ 9 ข้อโต้แย้ง: [, b1, b2, =, x, ==, z1, และz2]

[แยกวิเคราะห์ข้อโต้แย้งของมันในฐานะการแสดงออกตามเงื่อนไข อาร์กิวเมนต์ 9 ข้อนั้นไม่ได้รวมอยู่ในนิพจน์เงื่อนไขที่ถูกต้องดังนั้นจึงอาจส่งคืนข้อผิดพลาดได้

การ[[ ... ]]สร้างได้รับการแนะนำโดย Korn shell อาจประมาณปี 1988 ksh86aในปี 1987 ไม่ได้มีมันในขณะที่ksh88มีมันตั้งแต่เริ่มต้น

ข้าง ksh (การใช้งานทั้งหมด) [[...]]ได้รับการสนับสนุนโดย bash (ตั้งแต่รุ่น 2.02) และ zsh แต่การใช้งานทั้งสามนั้นแตกต่างกันและมีความแตกต่างระหว่างแต่ละเวอร์ชันของเชลล์เดียวกันแม้ว่าการเปลี่ยนแปลงจะเข้ากันได้โดยทั่วไป=~โอเปอเรเตอร์ที่รู้จักกันว่าจะทำลายสคริปต์บางตัวหลังจากเวอร์ชั่นที่แน่นอนเมื่อพฤติกรรมของมันเปลี่ยนไป) [[...]]ไม่ได้ระบุโดย POSIX หรือ Unix หรือ Linux (LSB) มันได้รับการพิจารณาเพื่อรวมสองสามครั้ง แต่ไม่รวมเป็นหน้าที่การใช้งานทั่วไปของมันที่ได้รับการสนับสนุนโดยเปลือกหอยใหญ่นั้นครอบคลุมโดย[คำสั่งและการcase-in-esacสร้าง

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

[[ ... ]]ไม่เกี่ยวกับความรู้==จากการเริ่มต้นและเทียบเท่ากับ1= ความผิดพลาดของ ksh คือ (และก่อให้เกิดความสับสนและข้อบกพร่องมากมาย) นั่นคือ=และ==ไม่ใช่ตัวดำเนินการความเสมอภาค แต่เป็นตัวดำเนินการจับคู่รูปแบบ (แม้ว่ารูปแบบการจับคู่สามารถปิดการใช้งานได้

ในรหัสของคุณด้านบน[[ $a == z* ]]เชลล์จะแยกวิเคราะห์ว่าเป็นโทเค็นหลาย ๆ ตัวในกฎที่คล้ายกับกฎทั่วไปยอมรับว่าเป็นการเปรียบเทียบการจับคู่รูปแบบถือว่าz*เป็นรูปแบบที่จะจับคู่กับเนื้อหาของaตัวแปร

โดยทั่วไปแล้วการถ่ายตัวเองด้วยเท้า[[ ... ]]ก็ยากกว่าการใช้[คำสั่ง แต่กฎบางอย่างเช่น

  • อ้างถึงตัวแปรเสมอ
  • ห้ามใช้ตัวดำเนินการ-aหรือ-o(ใช้[คำสั่งหลายคำสั่งและตัวดำเนินการ&&และ|| เชลล์ )

สร้าง[ความน่าเชื่อถือด้วย POSIX shells

[[...]]ในเชลล์ที่แตกต่างกันรองรับโอเปอเรเตอร์พิเศษเช่น-nt, regexp จับคู่โอเปอเรเตอร์ ... แต่รายการและลักษณะการทำงานนั้นแตกต่างกันไปจากเชลล์หนึ่งไปยังเชลล์และรุ่นต่อรุ่น

ดังนั้นถ้าคุณไม่ทราบว่าเชลล์และเวอร์ชันต่ำสุดของสคริปต์ของคุณจะถูกตีความด้วยวิธีใดมันอาจจะปลอดภัยกว่าหากใช้[คำสั่งมาตรฐาน


1ข้อยกเว้น: [[...]]เพิ่มลงใน bash ในเวอร์ชัน2.02แล้ว จนกว่า2.03จะมีการเปลี่ยนแปลง[[ x = '?' ]]จะกลับเป็นจริงในขณะที่[[ x == '?' ]]จะกลับเท็จ ที่มีการอ้างไม่ได้ป้องกันการจับคู่รูปแบบเมื่อใช้=ประกอบในรุ่นนั้น ==แต่เมื่อใช้


ข้อมูลที่ดี คุณช่วยอธิบายได้ไหมว่าทำไม "ไม่เคยใช้ตัวดำเนินการ -a หรือ -o"
grebneke

@grebneke ลอง[ '!' = foo -o a = a ]ในbashตัวอย่างเช่น
Stéphane Chazelas

0

ทั้งสองถูกใช้เพื่อประเมินนิพจน์และ [[จะไม่ทำงานกับ POSIX bourn เชลล์ที่เก่ากว่าและ [[ยังรองรับการจับคู่รูปแบบและ regex ตัวอย่างลองสิ่งเหล่านี้

[ $n -eq 0 -a $y -eq 0 ] && echo "Error" || echo "Ok"

[[ $n -eq 0 && $y -eq 0 ]] && echo "Error" || echo "Ok"


โปรดทราบว่าเชลล์เป้าหมายไม่ได้เป็น POSIX [[...]]รองรับ regexps เฉพาะในบางรุ่นของเชลล์บางรุ่นเช่นเดียวกับบางรุ่นที่[รองรับการจับคู่ regexp สำหรับการเปรียบเทียบทางคณิตศาสตร์ในเปลือกหอยที่สนับสนุน[[คุณควรเขียน(( n == 0 && y == 0))
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.