ทำไมไม่ `|` ได้รับการปฏิบัติอย่างแท้จริงในรูปแบบกลม?


13

คำถามของฉันมาจากการจัดเก็บการแสดงออกปกติในตัวแปรเปลือกหลีกเลี่ยงปัญหาเกี่ยวกับการอ้างอิงตัวอักษรที่พิเศษให้กับเชลล์ได้อย่างไร .

  1. ทำไมมีข้อผิดพลาด:

    $ [[ $a = a|b ]]  
    bash: syntax error in conditional expression: unexpected token `|'
    bash: syntax error near `|b'

    ภายใน[[ ... ]]ตัวถูกดำเนินการตัวที่สองของ=คาดว่าจะเป็นรูปแบบกลม

    คือa|bไม่ใช่รูปแบบ globbing ที่ถูกต้อง? คุณช่วยชี้ให้เห็นว่ามันละเมิดกฎไวยากรณ์หรือไม่

  2. ความคิดเห็นบางส่วนด้านล่างชี้ให้เห็นว่า|ถูกตีความว่าเป็นไปป์

    แล้วเปลี่ยน=สำหรับรูปแบบ glob ไป=~สำหรับ regex รูปแบบทำให้ |การทำงาน

    $ [[ $a =~ a|b ]]

    ฉันเรียนรู้จากLearning Bash p180 ในโพสต์ก่อนหน้าของฉันซึ่ง|เป็นที่รู้จักในฐานะจุดเริ่มต้นของการตีความแม้กระทั่งก่อนการตีความขั้นตอนอื่น ๆ (รวมถึงการแยกวิเคราะห์การแสดงออกตามเงื่อนไขในตัวอย่าง) ดังนั้น |จะรู้ได้อย่างไรว่าตัวดำเนินการ regex เมื่อใช้=~โดยไม่ถูกจดจำว่าเป็นไพพ์ในการใช้งานที่ไม่ถูกต้องเช่นเดียวกับเมื่อใช้=? นั่นทำให้ฉันคิดว่าข้อผิดพลาดทางไวยากรณ์ในตอนที่ 1 ไม่ได้|แปลว่าไพพ์

    แต่ละบรรทัดที่เชลล์อ่านจากอินพุตมาตรฐานหรือสคริปต์เรียกว่าไพพ์ไลน์ มันมีคำสั่งอย่างน้อยหนึ่งคำคั่นด้วยศูนย์หรือมากกว่าตัวอักษรท่อ (|) สำหรับแต่ละไปป์ไลน์ที่อ่านเชลล์จะแบ่งออกเป็นคำสั่งตั้งค่า I / O สำหรับไปป์ไลน์จากนั้นทำดังต่อไปนี้สำหรับแต่ละคำสั่ง (รูปที่ 7-1):

ขอบคุณ


1
ทราบว่าในบางรุ่นทุบตี extglob แยก (ที่|เป็นพิเศษ) อยู่บนโดยค่าเริ่มต้น[[ $var = $pattern ]]ในด้านขวามือของ มันจะน่าสนใจที่จะแยกรุ่นและshoptการกำหนดค่าตัวเลือกที่พฤติกรรมนี้เห็น - ถ้าเป็นเพียงที่ที่extglobเปิดอยู่โดยค่าเริ่มต้นหรือการกำหนดค่าที่ชัดเจนดีเรามี
ชาร์ลส์ดัฟฟี่

2
BTW ถ้าคุณต้องการที่จะแยกแยะกรณีของตัวละครไปป์ที่รบกวนการแยกวิเคราะห์ขั้นตอนก่อนหน้า (ซึ่งฉันเห็นด้วยไม่ได้เกิดขึ้น แต่ไม่ชัดเจนสำหรับผู้อ่านเท่าที่ควร) ใช้pattern='a|b'แล้วขยาย$patternunquote บน RHS
Charles Duffy

@CharlesDuffy นั่นคือประเด็นที่เกิดขึ้นในQ&Aซึ่งคำถามนี้เป็นคำถามที่ตามมา
Stéphane Chazelas

อ่า - บริบทนั้นสมเหตุสมผล และคำตอบของคุณที่นี่ยอดเยี่ยม ขอบคุณทั้งสองอย่าง
Charles Duffy

ทิมแยกคำตอบใด ๆ ด้านล่างตอบคำถามของคุณหรือไม่ โปรดพิจารณาการยอมรับหากเป็นเช่นนั้น ขอขอบคุณ!
Jeff Schaller

คำตอบ:


13

ไม่มีเหตุผลที่ดีว่าทำไม

[[ $a = a|b ]]

ควรรายงานข้อผิดพลาดแทนที่จะทดสอบว่า $ a เป็นa|bสตริงหรือ[[ $a =~ a|b ]]ไม่ในขณะที่ไม่ส่งคืนข้อผิดพลาด

เหตุผลเดียวก็|คือโดยทั่วไปแล้ว (ภายนอกและภายใน[[ ... ]]) เป็นอักขระพิเศษ ใน[[ $a =ตำแหน่งนั้นbashคาดว่าจะมีโทเค็นประเภทหนึ่งซึ่งเป็นWORDปกติเช่นอาร์กิวเมนต์หรือเป้าหมายของการเปลี่ยนเส้นทางในบรรทัดคำสั่งเชลล์ปกติ (แต่ราวกับว่าextglobมีการเปิดใช้งานตัวเลือกตั้งแต่ทุบตี 4.1)

(โดยWORDที่นี่ฉันหมายถึงคำในไวยากรณ์เชลล์สมมุติฐานเช่นที่อธิบายโดยข้อกำหนด POSIXซึ่งเป็นสิ่งที่เชลล์จะแยกวิเคราะห์เป็นโทเค็นเดียวในบรรทัดคำสั่งเชลล์แบบง่ายไม่ใช่คำจำกัดความอื่น ๆ เช่นภาษาอังกฤษ หนึ่งในลำดับของตัวอักษรหรือลำดับของอักขระที่ไม่ใช่ระยะห่างที่. foo"bar baz", $(echo x y)มีสองเช่นWORD s)

ในบรรทัดคำสั่งเชลล์ปกติ:

echo a|b

เป็นประปาecho a ไม่ได้เป็นคำก็สามราชสกุลกคำเป็นโทเค็นและคำโทเค็นba|ba |b

เมื่อนำมาใช้[[ $a = a|b ]], bashคาดว่าจะมีคำที่จะได้รับ ( a) แต่แล้วก็พบว่าไม่คาดคิด|โทเค็นซึ่งทำให้เกิดข้อผิดพลาด

ที่น่าสนใจbashไม่บ่นใน:

[[ $a = a||b ]]

เพราะตอนนี้มันเป็นaโทเค็นแล้วตามด้วย||โทเค็นตามด้วยbดังนั้นจึงแยกวิเคราะห์แบบเดียวกับ:

[[ $a = a || b ]]

ซึ่งการทดสอบนั่น$aคือaหรือว่าbสตริงไม่ว่างเปล่า

ตอนนี้ใน:

[[ $a =~ a|b ]]

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

[[ $a =~ 'a|b' ]]

ไม่ตรงกับa|bregexp อีกต่อไปแต่เทียบกับa\|bregexp นั่นคือการอ้างเปลือกมีผลข้างเคียงของการลบความหมายพิเศษของผู้ประกอบการ regexp มันเป็นคุณสมบัติดังนั้นพฤติกรรมจะคล้ายกับ[[ $a = "?" ]]รูปแบบหนึ่ง แต่รูปแบบสัญลักษณ์ (ใช้ใน[[ $a = pattern ]]) คือเชลล์WORDS (ใช้ในตัวอย่างเช่น) ในขณะที่ regexps ไม่ใช่

ดังนั้นbashมีการรักษาทุกผู้ประกอบการ regexp ขยายที่เป็นอย่างอื่นได้ตามปกติอักขระพิเศษเปลือกเช่น|, (, )แตกต่างกันเมื่อแยกวิเคราะห์ข้อโต้แย้งของที่=~ผู้ประกอบการ

ยังทราบว่าในขณะที่

 [[ $a =~ (ab)*c ]]

ตอนนี้ใช้งานได้

 [[ $a =~ [)}] ]]

ไม่ คุณต้องการ:

 [[ $a =~ [\)}] ]]
 [[ $a =~ [')'}] ]]

ซึ่งในเวอร์ชันก่อนหน้านี้bashจะจับคู่กับแบ็กสแลชไม่ถูกต้อง อันนั้นได้รับการแก้ไขแล้ว แต่

 [[ $a =~ [^]')'] ]]

ไม่ได้ตรงกับในทับขวาอย่างที่ควรเช่น เพราะbashล้มเหลวที่จะตระหนักว่า)อยู่ภายในวงเล็บเพื่อหนี)ที่จะส่งผลใน[^]\)]regexp ที่ตรงกับตัวอักษรใด ๆ แต่], และ\)

ksh93 มีข้อบกพร่องที่เลวร้ายกว่ามากที่ด้านหน้า

ในzshมันเป็นคำของเชลล์ปกติที่คาดหวังและการอ้างอิงตัวดำเนินการ regexp ไม่มีผลต่อความหมายของตัวดำเนินการ regexp

[[ $a =~ 'a|b' ]]

คือการจับคู่กับa|bregexp

นั่นหมายความว่า=~สามารถเพิ่มไปยังคำสั่ง[/ ได้test:

[ "$a" '=~' 'a|b' ]
test "$a" '=~' 'a|b'

(ยังใช้งานyashได้=~ต้องมีการอ้างถึงในzshฐานะที่=somethingเป็นตัวดำเนินการเชลล์พิเศษที่นั่น)

ทุบตี 3.1 zshใช้ในการประพฤติชอบ มันเปลี่ยนใน 3.2 สันนิษฐานว่าเพื่อให้สอดคล้องกับksh93(แม้ว่าbashเป็นเปลือกที่แรกขึ้นมาด้วย[[ =~ ]]) แต่คุณยังสามารถทำBASH_COMPAT=31หรือshopt -s compat31จะกลับไปสู่พฤติกรรมที่ผ่านมา (ยกเว้นในขณะที่[[ $a =~ a|b ]]จะกลับข้อผิดพลาดในbash3.1 ก็ไม่ได้อีกต่อไป ในbash -O compat31ด้วยรุ่นที่ใหม่กว่าbash)

หวังว่ามันจะอธิบายได้อย่างชัดเจนว่าทำไมฉันถึงพูดว่ากฎนั้นสับสนและทำไมต้องใช้

[[ $a =~ $var ]]

ช่วยรวมกับความสามารถในการพกพาไปยังเชลล์อื่น ๆ


zsh [[ $a = a|b ]]นอกจากนี้ยังมีการรายงานข้อผิดพลาดใน
NotAnUnixNazi

@isaac ใช่ว่าเป็นจุดที่ฉันทำที่นี่ a|bไม่ได้เป็นเปลือกWORDนี่มันเป็นa, |และbโทเค็น Like echo a|bไม่ได้ส่งออกa|bหรือไม่ขยายa|bglob คุณต้องพูดว่า|มันเป็นอักขระเปลือกพิเศษที่ไม่ถูกต้องในบริบทนั้น [[ $a = (a|b) ]]จะทำงานเหมือนecho (a|b)จะทำงานตามที่(a|b)เป็นตัวดำเนินการตัวแทน zsh
Stéphane Chazelas

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

11

globs มาตรฐาน ( "การขยายตัวของชื่อไฟล์") คือ: *, และ? ไม่ใช่ตัวดำเนินการ glob ที่ถูกต้องในการตั้งค่ามาตรฐาน (ไม่ใช่ extglob)[ ... ]|

ลอง:

shopt -s extglob
[[ a = @(a|b) ]] && echo matched

1
ขอบคุณ แต่ทำไม|intepereted แท้จริงไม่? ทำไมจึงมีข้อผิดพลาดทางไวยากรณ์?
ทิม

1
มันไม่ได้ยกมา
Jeff Schaller

3
ในการตั้งค่ามาตรฐาน|ไม่ใช่ตัวดำเนินการแบบกลมดังนั้นจึงไม่ถูก|ตีความอย่างแท้จริงโดยไม่มีการอ้างถึง? เหตุใดจึงมีข้อผิดพลาดทางไวยากรณ์
ทิม

1
|เป็นตัวควบคุม มันไม่เคยถือว่าเป็นตัวอักษรในลักษณะเดียวกับตัวอักษรหรือตัวเลข
chepner

3
เนื่องจากในโหมดนั้นเชลล์ไม่ได้คาดหวังว่าจะมีการเปลี่ยนเส้นทางไปป์ที่อยู่ตรงกลางของ [-] ที่ยังไม่ปิด [[ $a = aไม่ใช่คำสั่งที่ถูกต้องที่สามารถส่งเอาต์พุตไปยังกระบวนการอื่น (อย่างน้อยนั่นคือสิ่งที่เชลล์คิดว่าคุณพยายามทำ)
Jason C

5

หากคุณต้องการให้ regex ตรงกับการทดสอบ:

[[ "$a" =~ a|b ]]

@Tim คุณควรเปิดคำถามใหม่ไม่ใช่แก้ไขคำถามปัจจุบันของคุณอย่างต่อเนื่อง
Gardenhead

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