การแสดงออกวงเล็บ (ไม่มีช่วง) จับคู่ตัวละครที่ไม่คาดคิดในทุบตี


20

ฉันใช้ทุบตีบน Linux ฉันได้รับความสำเร็จจากคำสั่ง if ต่อไปนี้ แต่ไม่ควรส่งคืนรหัสที่ล้มเหลวหรือ

if [[  = [⅕⅖⅗] ]] ; then echo yes ; fi

สี่เหลี่ยมจัตุรัสไม่เท่ากับอักขระใด ๆ ดังนั้นฉันจึงไม่เห็นสาเหตุที่ฉันได้รหัสสำเร็จ

เป็นสิ่งสำคัญสำหรับฉันที่จะเก็บวงเล็บคู่ไว้ในกระเป๋า

มีวิธีอื่นที่จะทำช่วงในสถานการณ์นี้หรือคำแนะนำอื่น ๆ ?


2
อาจเป็นผลมาจากตัวละครทั้งหมดที่มีคำสั่งการเรียงลำดับที่ไม่ได้กำหนดในสถานที่ของคุณ (และการเรียงลำดับเดียวกัน) ดูอย่างต่อเนื่องการอภิปรายในกลุ่มออสติน เปลี่ยนสถานที่ไปยัง C ที่จะแก้ไขได้
Stéphane Chazelas

1
ขออภัยCจะไม่ทำที่นี่เนื่องจากไม่ใช่อักขระไบต์เดียว C.UTF-8จะทำในที่ที่มีอยู่
Stéphane Chazelas

11
ขอแสดงความยินดีคุณสามารถเรียกใช้Stéphaneในหัวข้อ Austin Group ในคำถามแรกของคุณได้ ต้องมีค่าอย่างน้อย⅗ของ Internets หรือ⅘หรือแม้กระทั่ง■อินเตอร์เน็ตดูเหมือนว่าจะเหมือนกัน ยินดีต้อนรับสู่Unix & Linuxและโปรดนำคำถามที่น่าสนใจมาด้วย
Derobert

คำตอบ:


29

นั่นเป็นผลมาจากตัวละครเหล่านั้นมีลำดับการเรียงแบบเดียวกัน

คุณจะสังเกตได้ว่า

sort -u << EOF




EOF

ส่งคืนบรรทัดเดียวเท่านั้น

หรือว่า:

expr  = 

ผลตอบแทนจริง (ตามที่ต้องการโดย POSIX)

โลแคลส่วนใหญ่ที่มาพร้อมกับระบบ GNU มีจำนวนอักขระ (และลำดับของอักขระ (เรียงลำดับการเรียง)) ที่มีลำดับการเรียงแบบเดียวกัน ในกรณีของ■⅕⅖⅗อันนั้นเป็นเพราะลำดับไม่ได้ถูกกำหนดไว้และตัวละครที่ไม่ได้กำหนดลำดับท้ายจะมีลำดับการเรียงลำดับเดียวกันในระบบ GNU มีตัวละครที่ถูกกำหนดไว้อย่างชัดเจนว่ามีลำดับการจัดเรียงแบบเดียวกันเช่น Ș และ though (แม้ว่าจะไม่มีตรรกะจริงหรือความแน่นอนในการเรียงลำดับเหมือนกัน)

นั่นคือที่มาของพฤติกรรมที่น่าแปลกใจและปลอม ฉันได้หยิบยกประเด็นปัญหาขึ้นมาเมื่อเร็ว ๆ นี้ในกลุ่มจดหมายของ Austin (ส่วนหลัง POSIX และ Single UNIX Specification) รายชื่อผู้รับจดหมายและการอภิปรายยังคงดำเนินต่อไปในปี 2558-2558

ในกรณีนี้ไม่ว่า[y]จะตรงกับxที่xและyเรียงลำดับเดียวกันไม่ชัดเจนสำหรับฉัน แต่เนื่องจากการแสดงออกของวงเล็บปีกกามีความหมายเพื่อให้ตรงกับองค์ประกอบเรียงที่บ่งบอกว่าbashพฤติกรรมที่คาดหวัง

ในกรณีใด ๆ ผมคิดว่า[⅕-⅕]หรืออย่างน้อยก็ควรจะตรงกับ[⅕-⅖]

คุณจะสังเกตเห็นว่าเครื่องมือต่าง ๆ ทำงานแตกต่างกัน ksh93 มีพฤติกรรมเช่นbashGNU grepหรือsedไม่ กระสุนอื่น ๆ บางตัวมีพฤติกรรมแตกต่างกันyashไป

เพื่อให้มีพฤติกรรมที่สอดคล้องกันคุณต้องมีสถานที่ที่อักขระทุกตัวเรียงลำดับ โลแคล C เป็นแบบทั่วไป อย่างไรก็ตามชุดอักขระในโลแคล C บนระบบส่วนใหญ่คือ ASCII บนระบบ GNU โดยทั่วไปคุณสามารถเข้าถึงC.UTF-8โลแคลที่สามารถใช้แทนการทำงานกับอักขระ UTF-8 ได้

ดังนั้น:

(export LC_ALL=C.UTF-8; [[  = [⅕⅖⅗] ]])

หรือเทียบเท่ามาตรฐาน:

(export LC_ALL=C.UTF-8
 case  in ([⅕⅖⅗]) true;; (*) false; esac)

ควรกลับเท็จ

อีกทางเลือกหนึ่งคือการตั้งค่าLC_COLLATEเป็น C เท่านั้นซึ่งจะทำงานบนระบบ GNU แต่ไม่จำเป็นต้องอยู่ที่ตัวอื่นซึ่งมันอาจล้มเหลวในการระบุลำดับการเรียงของอักขระแบบหลายไบต์


บทเรียนหนึ่งของสิ่งนั้นคือความเท่าเทียมกันนั้นไม่ได้เป็นความคิดที่ชัดเจนอย่างที่ใคร ๆ คาดหวังเมื่อมันมาถึงการเปรียบเทียบสตริง ความเท่าเทียมกันอาจหมายถึงจากเข้มงวดที่สุดถึงเข้มงวดน้อยที่สุด

  1. จำนวนไบต์เท่ากันและองค์ประกอบไบต์ทั้งหมดมีค่าเท่ากัน
  2. จำนวนอักขระที่เท่ากันและอักขระทั้งหมดเหมือนกัน (ตัวอย่างเช่นอ้างถึง codepoint เดียวกันในชุดอักขระปัจจุบัน)
  3. สตริงทั้งสองมีลำดับการเรียงลำดับเหมือนกันตามอัลกอริทึมการเปรียบเทียบของโลแคล (นั่นคือ <b หรือ b> a ไม่เป็นจริง)

ตอนนี้สำหรับ 2 หรือ 3 ที่ถือว่าทั้งสองสตริงมีอักขระที่ถูกต้อง ใน UTF-8 และการเข้ารหัสอื่น ๆ บางลำดับของไบต์ไม่เกิดอักขระที่ถูกต้อง

1 และ 2 ไม่จำเป็นต้องเทียบเท่าเนื่องจากนั้นหรือเนื่องจากอักขระบางตัวอาจมีการเข้ารหัสที่เป็นไปได้มากกว่าหนึ่งรายการ โดยทั่วไปแล้วเป็นกรณีของการเข้ารหัส stateful เช่น ISO-2022-JP ซึ่งAสามารถแสดงเป็น41หรือ1b 28 42 41( 1b 28 42เป็นลำดับเพื่อสลับไปยัง ASCII และคุณสามารถแทรกได้มากเท่าที่คุณต้องการ แต่ก็ไม่ได้สร้างความแตกต่าง) จะไม่คาดหวังว่าการเข้ารหัสประเภทนั้นจะยังคงถูกใช้งานอยู่และเครื่องมือของ GNU อย่างน้อยโดยทั่วไปจะไม่ทำงานอย่างถูกต้องกับมัน

นอกจากนี้ระวังว่ายูทิลิตี้ที่ไม่ใช่ GNU ส่วนใหญ่ไม่สามารถจัดการกับค่า 0 ไบต์ (อักขระ NUL ใน ASCII)

ซึ่งในบรรดาคำจำกัดความถูกนำมาใช้ขึ้นอยู่กับสาธารณูปโภคและยูทิลิตี้การดำเนินงานหรือรุ่น POSIX ไม่ชัดเจน 100% ในโลแคล C ทั้ง 3 รายการเทียบเท่ากัน ด้านนอกของ YMMV นั้น


อีกกรณีทั่วไปที่ 1 และ 2 แตกต่างกันเป็นUnicodeกับสิ่งต่าง ๆ เช่นการรวมอักขระ
Gilles 'ดังนั้นหยุดความชั่วร้าย'

@Gilles การรวมอักขระเป็นอักขระของตนเอง การรวมกันในรูปแบบกราฟ / เซลล์ แต่ยังคงมีตัวละครหลายตัว é (U + 00E9) และé (e ตามด้วย U + 0301) เป็นกราฟเดียวกัน แต่สองลำดับอักขระที่แตกต่างกัน (อย่างน้อยจาก POSIX APIs มุมมอง) ภายในวันที่ 1 และ 2 พวกเขาจะแตกต่างกัน เมื่อถึง 3 พวกเขาอาจพิจารณาเหมือนกันหาก U + 0301 มีน้ำหนักการเรียงทั้งหมดตั้งไว้ที่ "IGNORE" แต่โดยทั่วไปแล้วไม่ใช่กรณีที่คนทั่วไปต้องการตัดสินใจตามลำดับกำกับ
Stéphane Chazelas

มันมักจะเป็นที่พึงปรารถนาที่จะต้องพิจารณาéและจะเป็นสายเดียวกัน eแต่ไม่ได้ ความคิดของการเรียงลำดับของ POSIX ไม่ค่อยถูกต้องมันมีพื้นฐานมาจากตัวอักษรมากเกินไปและไม่ได้คำนึงถึงวิธีการเรียงลำดับสตริงที่พบบ่อยที่สุด (เช่นพจนานุกรมภาษาฝรั่งเศสไม่ได้ใช้คำสั่งพจนานุกรมเพื่อเรียงลำดับคำ: พวกมันผ่าน lexicographic จากนั้นใช้สำเนียงเพื่อตัดสินใจความสัมพันธ์)
Gilles 'หยุดความชั่วร้าย'

@Gilles ใช่ นั่นเป็นเหตุผลว่าทำไมฉันถึงบอกว่าตัวละครเหล่านั้นมีลำดับการจัดเรียงเดียวกัน (เจตนา) ในสถานที่ glibc มีเหตุผลเล็กน้อย é vs éมักจะได้รับการแก้ไขด้วยการทำการแปลงบางอย่างบนสตริงก่อนเช่นการสลายตัวแบบบัญญัติ (คล้ายกับการแปลงเป็นตัวพิมพ์เล็กก่อนเมื่อคุณต้องการเรียงลำดับ / จับคู่แบบตัวพิมพ์เล็ก) ดูคู่มือ ICUสำหรับการอ้างอิงที่ดีเกี่ยวกับเรื่องนี้
Stéphane Chazelas

@Gilles น้ำหนักในอัลกอริทึมการเปรียบเทียบโลแคล POSIX สามารถทำการเรียงลำดับพจนานุกรมภาษาฝรั่งเศสนั้นได้ นั่นเป็นวิธีที่น้ำหนักทำงาน บัตรผ่านใบแรกใช้น้ำหนักหลัก (โดยที่ e และé (และ E และÉ) มีเหมือนกันและการรวมกันของสำเนียงเฉียบพลันจะถูกละเว้น) การผ่านครั้งที่สอง (ถ้าเท่ากัน) จะตรวจสอบสำเนียงซึ่งเป็นตัวพิมพ์ใหญ่ครั้งที่ 3 ...
Stéphane Chazelas

-3

คุณกำลังทำผิด=และ==ไม่เหมือนกัน

ลองตัวอย่างเหล่านี้:

if [[ "■" == "[⅕⅖⅗]" ]] ; then echo yes ; else echo no ; fi

if [[ "1" == "1" ]] ; then echo yes ; else echo no ; fi

if [[ "■" == "■" ]] ; then echo yes ; else echo no ; fi

1
ที่ไม่เป็นความจริง. POSIX ระบุว่า=ควรใช้โอเปอเรเตอร์เพื่อตรวจสอบความเท่าเทียมกัน ปัญหาคือคำพูดที่หายไปไม่ใช่ผู้ประกอบการ
scai

1
ยังman bashกล่าวใน[[ส่วน: "ตัวดำเนินการ = นั้นเทียบเท่ากับ =="
michas

1
@scai, POSIX ไม่ได้ระบุ[[...]]ผู้ให้บริการ และ = และ == เหมือนกันในเชลล์ที่มีการใช้งาน (ksh / bash / zsh) และสำหรับการจับคู่รูปแบบไม่ใช่ความเท่าเทียมกัน
Stéphane Chazelas

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