POSIX ต้องการอะไรสำหรับเอกสารที่ยกมาที่นี่ภายในการทดแทนคำสั่ง?


20

ในคำถามนี้มีคนรายงานปัญหาการใช้ที่นี่เอกสารด้วยคำคั่นอ้างภายใน$(...)แทนคำสั่งที่เครื่องหมาย\ที่ท้ายบรรทัดภายในการเรียกเอกสารการขึ้นบรรทัดใหม่เข้าสู่เส้นต่อเนื่องขณะเดียวกันที่นี่เอกสารนอกงานคำสั่งเปลี่ยนตัวตามที่คาดไว้ .

นี่คือตัวอย่างเอกสารที่ง่ายขึ้น:

cat <<'EOT'
abc ` def
ghi \
jkl
EOT

ซึ่งรวมถึง backtick หนึ่งรายการและแบ็กสแลชหนึ่งรายการที่ท้ายบรรทัด ตัวคั่นถูกยกมาดังนั้นจึงไม่มีการขยายเกิดขึ้นภายในร่างกาย ใน Bourne-alikes ทั้งหมดฉันสามารถค้นหาผลลัพธ์นี้เป็นคำต่อคำเนื้อหา หากฉันวางเอกสารเดียวกันไว้ภายในการทดแทนคำสั่งดังนี้:

x=$(cat <<'EOT'
abc ` def
ghi \
jkl
EOT
)
echo "$x"

จากนั้นพวกเขาจะไม่ทำงานเหมือนกันอีกต่อไป:

  • dash, ash, zsh, ksh93, BusyBox ash, mkshและ SunOS 5.10 POSIX shทั้งหมดให้เนื้อหาคำต่อคำของเอกสารเป็นมาก่อน
  • Bash 3.2 ให้ข้อผิดพลาดทางไวยากรณ์สำหรับ backtick ที่ไม่ตรงกัน ด้วย backticks ที่ตรงกันจะพยายามเรียกใช้เนื้อหาเป็นคำสั่ง
  • Bash 4.3 จะยุบ "ghi" และ "jkl" ลงในบรรทัดเดียว แต่ไม่มีข้อผิดพลาด --posixตัวเลือกที่ไม่ส่งผลกระทบนี้ Kusalananda บอกฉัน (ขอบคุณ!) ที่pdkshทำงานในลักษณะเดียวกัน

ในคำถามเดิมฉันบอกว่านี่เป็นข้อผิดพลาดในตัวแยกวิเคราะห์ของ Bash ใช่ไหม? [อัพเดต: ใช่ ] ข้อความที่เกี่ยวข้องจาก POSIX (ทั้งหมดมาจากคำจำกัดความภาษาของคำสั่งเชลล์) ที่ฉันสามารถค้นหาได้คือ:

  • stitution2.6.3 การทดแทนคำสั่ง :

    ด้วยรูปแบบ $ (คำสั่ง) อักขระทั้งหมดที่ตามหลังวงเล็บเปิดไปยังวงเล็บปิดที่ตรงกันจะประกอบด้วยคำสั่ง เชลล์สคริปต์ที่ถูกต้องใด ๆ สามารถใช้สำหรับคำสั่งยกเว้นสคริปต์ที่มีเพียงการเปลี่ยนเส้นทางซึ่งให้ผลลัพธ์ที่ไม่ระบุ

  • §2.7.4ที่นี่เอกสาร :

    หากส่วนใดส่วนหนึ่งของคำถูกยกมาตัวคั่นจะต้องเกิดขึ้นจากการลบคำพูดในคำและบรรทัดที่นี่เอกสารจะไม่ถูกขยาย

  • Escape2.2.1 Escape Character (แบ็กสแลช) :

    หาก <newline> ตามหลัง <slashash> เชลล์จะตีความว่านี่เป็นความต่อเนื่องของบรรทัด <backslash> และ <newline> จะถูกลบออกก่อนที่จะแยกอินพุตเป็นโทเค็น

  • §2.3การจดจำโทเค็น :

    เมื่อโทเค็นio_hereได้รับการยอมรับโดยไวยากรณ์ (ดูเชลล์ไวยากรณ์ ) หนึ่งหรือมากกว่าหนึ่งบรรทัดต่อมาทันทีหลังจากโทเค็นNEWLINEถัดไปในรูปแบบเนื้อหาของเอกสารที่นี่หนึ่งหรือมากกว่าและจะถูกแยกตามกฎของที่นี่ - เอกสาร

    เมื่อไม่ได้ประมวลผลio_hereเชลล์จะแบ่งอินพุตเป็นโทเค็นโดยใช้กฎที่บังคับใช้ครั้งแรกด้านล่างกับอักขระถัดไปในอินพุต ...

    ...

    1. หากอักขระปัจจุบันคือ <backslash> เครื่องหมายคำพูดเดี่ยวหรือเครื่องหมายคำพูดคู่และไม่ได้ยกมามันจะส่งผลกระทบต่อการอ้างอิงสำหรับอักขระที่ตามมาจนถึงส่วนท้ายของข้อความที่ยกมา กฎสำหรับข้อความที่มีการอธิบายไว้ในQuoting ในระหว่างการรับรู้โทเค็นจะไม่มีการแทนที่จริงและโทเค็นผลลัพธ์จะต้องมีตัวอักษรที่ปรากฏในอินพุต (ยกเว้นการเข้าร่วม <newline>) ที่ไม่ได้แก้ไขรวมถึงเครื่องหมายอัญประกาศหรือตัวดำเนินการทดแทนระหว่างและท้าย ของข้อความที่ยกมา

การตีความของฉันเกี่ยวกับเรื่องนี้ก็คือตัวละครทุกตัวหลังจากนั้น$(จนกระทั่งการยุติ)ประกอบด้วยเชลล์สคริปต์คำต่อคำ; เอกสารที่นี่ปรากฏขึ้นดังนั้นการประมวลผลเอกสารที่นี่จะเกิดขึ้นแทนโทเค็นทั่วไป เอกสารที่นี่มีตัวคั่นที่ยกมาซึ่งหมายความว่าเนื้อหาของมันจะถูกประมวลผลคำต่อคำ; และตัวละครหนีไม่เคยเข้ามา ฉันสามารถดูการโต้แย้งได้ว่ากรณีนี้ไม่ได้รับการแก้ไขและพฤติกรรมทั้งสองนั้นได้รับอนุญาต เป็นไปได้ว่าฉันได้ข้ามข้อความที่เกี่ยวข้องบางแห่งด้วยเช่นกัน


  • สถานการณ์นี้ชัดเจนขึ้นที่อื่นหรือไม่
  • สคริปต์แบบพกพาที่ควรพึ่งพา (ในทางทฤษฎี) คืออะไร
  • การรักษาเฉพาะที่ได้รับจากกระสุนเหล่านี้ (Bash 3.2 / Bash 4.3 / คนอื่น ๆ ) เป็นไปตามมาตรฐานหรือไม่? พระราชวังต้องห้าม? ได้รับอนุญาต?

คุณช่วยบอกเราได้ว่าคุณผลิตผลของคุณในกรณีที่สองได้อย่างไร?
Julie Pelletier

@JuliePelletier echo "$x"แต่วิธีการตรวจสอบการทำงานของตัวแปร ฉันแก้ไขบรรทัดนั้นลงด้านล่าง
Michael Homer

2
ดูเหมือนว่าเป็นการแก้ไขที่ง่าย โปรแกรมปรับปรุงนี้ทำงานอย่างน้อย: ละเว้น_quoted_newline_in_quoted_heredoc.patch
geirha

1
ฉันคิดว่าคุณกำลังตีความอย่างถูกต้องและ imo มาตรฐานค่อนข้างชัดเจนตั้งแต่"เชลล์จะขยายการทดแทนคำสั่งโดยการดำเนินการคำสั่งในสภาพแวดล้อม subshell [... ] และแทนที่การแทนที่คำสั่ง [... ] ด้วยผลลัพธ์มาตรฐานของ คำสั่ง [... ] "ดังนั้นมันจึงรันคำสั่งใน subshell และแทนที่$(...)ด้วยสิ่งที่เอาท์พุทคือ ... ตอนนี้เมื่อใช้คำสั่งในตัวอย่างของคุณใน subshell (ในbash) มันจะออกผลลัพธ์ที่คาดหวัง เมื่อเปลี่ยนเป็นคำสั่งการแทนที่เท่านั้นก็จะยุบ "ghi" และ "jkl" ดังนั้นนี่คือข้อผิดพลาด imo
don_crissti

2
@geirha ฉันรายงานข้อผิดพลาดของ Bash ; ฉันจะไม่สนใจเกี่ยวกับ pdksh เนื่องจากดูเหมือนจะไม่มีเงาแม้แต่การบำรุงรักษาในปัจจุบัน
Michael Homer

คำตอบ:


5

สิ่งนี้ถูกถามในรายชื่อผู้รับจดหมายของ Bash และผู้ดูแลยืนยันว่าเป็นข้อผิดพลาด

พวกเขายังกล่าวด้วยว่าข้อความใน POSIX "ไม่จำเป็นต้องคลุมเครือ แต่จำเป็นต้องอ่านอย่างใกล้ชิด" ดังนั้นฉันจึงขอคำชี้แจงเกี่ยวกับเรื่องนี้ คำตอบรวมถึงรายละเอียดของปัญหาและการตีความมาตรฐานมีดังนี้

การทดแทนคำสั่งคือปลาเฮอริ่งแดง มันเกี่ยวข้องเฉพาะกับที่มันชี้ให้เห็นว่าข้อผิดพลาดอยู่ที่ไหน

ตัวคั่นของเอกสารที่นี่จะถูกยกมาดังนั้นเส้นจะไม่ขยาย ในกรณีนี้เชลล์อ่านบรรทัดจากอินพุตราวกับว่าถูกอ้างถึง หากแบ็กสแลชปรากฏในบริบทที่มีการอ้างถึงมันจะไม่ทำหน้าที่เป็นตัวละครเลี่ยง (ดูด้านล่าง) และการจัดการแบ็กสแลช - ขึ้นบรรทัดใหม่จะไม่เกิดขึ้น ในความเป็นจริงถ้าส่วนใดส่วนหนึ่งของตัวคั่นถูกอ้างถึงบรรทัดเอกสารที่นี่จะถูกอ่านราวกับว่ามีการเสนอราคาเดียว

ข้อความใน Posix 2.2.1 เขียนขึ้นอย่างเชื่องช้า แต่หมายความว่าแบ็กสแลชจะได้รับการดูแลเป็นพิเศษเฉพาะเมื่อไม่มีการอ้างอิง คุณสามารถอ้างอิงเครื่องหมายแบ็กสแลชและยับยั้งการขยายทั้งหมดทั้งหมดได้ด้วยเครื่องหมายคำพูดเดี่ยวหรือแบ็กสแลชอื่น

ส่วนที่ปิดอ่านคือข้อความ "ไม่ขยาย" ซึ่งหมายถึงคำพูดเดียว มาตรฐานระบุไว้ใน 2.2 ว่าที่นี่เอกสารเป็น "รูปแบบอื่นของการอ้างอิง" แต่รูปแบบเดียวของการอ้างอิงที่คำไม่ได้ขยายเลยคือคำพูดเดียว ดังนั้นมันจึงเป็นรูปแบบของการอ้างที่เป็นเหมือนคำพูดเดียว แต่ไม่ใช่คำพูดเดียว


@Scott (1) ฉันเชื่อว่านี่ตอบคำถามทุกข้อและไม่มีอะไรฟุ่มเฟือย ความคิดเห็นของฉันที่เริ่มต้นจากคำตอบนั้นเกี่ยวกับการลบโดยผู้ดำเนินรายการซึ่งเข้าใจผิดสถานการณ์ (2) ฉันไม่มีชื่อเสียงเพียงพอ (3) ฉันจะได้ชื่นชมพฤติกรรมที่คล้ายกันโดยผู้ลบคำตอบของฉัน แต่ฉันจะจำไว้ว่าในอนาคต ขอบคุณสำหรับความคิด
เควิน

ประเด็นของฉันคือย่อหน้าแรกของคุณส่วนใหญ่เป็นการสนทนากับ Michael Mrozek และไม่ใช่คำตอบสำหรับคำถาม ฉันรู้ว่าคุณไม่มีชื่อเสียงพอที่จะแสดงความคิดเห็นในโพสต์ใด ๆ แต่ฉันเชื่อว่าคุณมีเมตาและแชทเพียงพอ
สกอตต์

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