ในคำถามนี้มีคนรายงานปัญหาการใช้ที่นี่เอกสารด้วยคำคั่นอ้างภายใน$(...)
แทนคำสั่งที่เครื่องหมาย\
ที่ท้ายบรรทัดภายในการเรียกเอกสารการขึ้นบรรทัดใหม่เข้าสู่เส้นต่อเนื่องขณะเดียวกันที่นี่เอกสารนอกงานคำสั่งเปลี่ยนตัวตามที่คาดไว้ .
นี่คือตัวอย่างเอกสารที่ง่ายขึ้น:
cat <<'EOT'
abc ` def
ghi \
jkl
EOT
ซึ่งรวมถึง backtick หนึ่งรายการและแบ็กสแลชหนึ่งรายการที่ท้ายบรรทัด ตัวคั่นถูกยกมาดังนั้นจึงไม่มีการขยายเกิดขึ้นภายในร่างกาย ใน Bourne-alikes ทั้งหมดฉันสามารถค้นหาผลลัพธ์นี้เป็นคำต่อคำเนื้อหา หากฉันวางเอกสารเดียวกันไว้ภายในการทดแทนคำสั่งดังนี้:
x=$(cat <<'EOT'
abc ` def
ghi \
jkl
EOT
)
echo "$x"
จากนั้นพวกเขาจะไม่ทำงานเหมือนกันอีกต่อไป:
dash
,ash
,zsh
,ksh93
, BusyBoxash
,mksh
และ SunOS 5.10 POSIXsh
ทั้งหมดให้เนื้อหาคำต่อคำของเอกสารเป็นมาก่อน- 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เชลล์จะแบ่งอินพุตเป็นโทเค็นโดยใช้กฎที่บังคับใช้ครั้งแรกด้านล่างกับอักขระถัดไปในอินพุต ...
...
- หากอักขระปัจจุบันคือ <backslash> เครื่องหมายคำพูดเดี่ยวหรือเครื่องหมายคำพูดคู่และไม่ได้ยกมามันจะส่งผลกระทบต่อการอ้างอิงสำหรับอักขระที่ตามมาจนถึงส่วนท้ายของข้อความที่ยกมา กฎสำหรับข้อความที่มีการอธิบายไว้ในQuoting ในระหว่างการรับรู้โทเค็นจะไม่มีการแทนที่จริงและโทเค็นผลลัพธ์จะต้องมีตัวอักษรที่ปรากฏในอินพุต (ยกเว้นการเข้าร่วม <newline>) ที่ไม่ได้แก้ไขรวมถึงเครื่องหมายอัญประกาศหรือตัวดำเนินการทดแทนระหว่างและท้าย ของข้อความที่ยกมา
การตีความของฉันเกี่ยวกับเรื่องนี้ก็คือตัวละครทุกตัวหลังจากนั้น$(
จนกระทั่งการยุติ)
ประกอบด้วยเชลล์สคริปต์คำต่อคำ; เอกสารที่นี่ปรากฏขึ้นดังนั้นการประมวลผลเอกสารที่นี่จะเกิดขึ้นแทนโทเค็นทั่วไป เอกสารที่นี่มีตัวคั่นที่ยกมาซึ่งหมายความว่าเนื้อหาของมันจะถูกประมวลผลคำต่อคำ; และตัวละครหนีไม่เคยเข้ามา ฉันสามารถดูการโต้แย้งได้ว่ากรณีนี้ไม่ได้รับการแก้ไขและพฤติกรรมทั้งสองนั้นได้รับอนุญาต เป็นไปได้ว่าฉันได้ข้ามข้อความที่เกี่ยวข้องบางแห่งด้วยเช่นกัน
- สถานการณ์นี้ชัดเจนขึ้นที่อื่นหรือไม่
- สคริปต์แบบพกพาที่ควรพึ่งพา (ในทางทฤษฎี) คืออะไร
- การรักษาเฉพาะที่ได้รับจากกระสุนเหล่านี้ (Bash 3.2 / Bash 4.3 / คนอื่น ๆ ) เป็นไปตามมาตรฐานหรือไม่? พระราชวังต้องห้าม? ได้รับอนุญาต?
echo "$x"
แต่วิธีการตรวจสอบการทำงานของตัวแปร ฉันแก้ไขบรรทัดนั้นลงด้านล่าง
$(...)
ด้วยสิ่งที่เอาท์พุทคือ ... ตอนนี้เมื่อใช้คำสั่งในตัวอย่างของคุณใน subshell (ในbash
) มันจะออกผลลัพธ์ที่คาดหวัง เมื่อเปลี่ยนเป็นคำสั่งการแทนที่เท่านั้นก็จะยุบ "ghi" และ "jkl" ดังนั้นนี่คือข้อผิดพลาด imo