เครื่องหมายคำพูดคู่ในทุบตีจับคู่กันอย่างไร


15

GNU bash 4.3.48ฉันใช้ พิจารณาสองคำสั่งต่อไปนี้ที่แตกต่างกันโดยเครื่องหมายดอลลาร์เดียวเท่านั้น

คำสั่ง 1:

echo "(echo " * ")"

คำสั่ง 2:

echo "$(echo " * ")"

ผลลัพธ์ของพวกเขาคือตามลำดับ

(echo  test.txt ppcg.sh )

และ

 * 

เห็นได้ชัดในกรณีแรก*คือก้อนกลมซึ่งหมายความว่าเครื่องหมายอัญประกาศแรกไปพร้อมกับที่สองในการสร้างคู่และที่สามและสี่ในรูปแบบคู่อื่น

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

มีกรณีอื่นนอกเหนือจากการ$()ก่อสร้างที่มีเครื่องหมายอัญประกาศไม่ตรงกับอันถัดไป แต่ซ้อนกันแทน? พฤติกรรมนี้ได้รับการบันทึกไว้อย่างดีหรือไม่และถ้าใช่ฉันจะหาเอกสารที่เกี่ยวข้องได้จากที่ใด


2
ที่เกี่ยวข้อง: คำพูดที่ไม่ใช้ Escape ทุบตีบนแทนคำสั่ง
G-Man กล่าวว่า 'Reinstate Monica'

อีกครั้งไวยากรณ์ไฮไลต์ใน UL SE ผิดและทำให้เข้าใจผิดแม้ว่า prettify JS คือตำหนิ
Weijun Zhou

คำตอบ:


14

โครงสร้างการซ้อนใด ๆ ที่สามารถสอดแทรกภายในสตริงสามารถมีสตริงเพิ่มเติมภายใน: พวกเขาจะถูกแยกวิเคราะห์เหมือนสคริปต์ใหม่จนถึงเครื่องหมายปิดและยังสามารถซ้อนกันหลายระดับลึก $หนึ่งในบาร์ทั้งหมดเริ่มต้นของผู้ที่มี ทั้งหมดนี้มีการจัดทำเอกสารโดยใช้ชุดข้อมูลจำเพาะภาษา Bash manual และ POSIX เชลล์คำสั่ง

มีบางกรณีของโครงสร้างเหล่านี้:

  • คำสั่งทดแทนด้วย$( ... )ตามที่คุณพบ POSIX ระบุพฤติกรรมนี้ :

    ด้วย$(command)รูปแบบอักขระทั้งหมดที่ตามหลังวงเล็บเปิดเพื่อจับคู่วงเล็บปิดที่ตรงกันประกอบด้วยคำสั่ง เชลล์สคริปต์ที่ถูกต้องสามารถใช้สำหรับคำสั่ง ...

    เครื่องหมายคำพูดเป็นส่วนหนึ่งของเชลล์สคริปต์ที่ใช้ได้ดังนั้นพวกเขาจึงอนุญาตโดยใช้ความหมายปกติ

  • คำสั่งเปลี่ยนตัวใช้`มากเกินไป
  • ว่า "คำว่า" องค์ประกอบของอินสแตนซ์ทดแทนพารามิเตอร์ขั้นสูงเช่น ${parameter:-word}ความหมายของ "คำว่า" เป็น :

    ลำดับของอักขระที่ถือว่าเป็นหน่วยโดยเชลล์

    - ซึ่งรวมถึงข้อความที่ยกมาและแม้กระทั่งคำพูดที่ผสมa"b"c'd'e- แม้ว่าพฤติกรรมที่แท้จริงของการขยายตัวเป็นเสรีนิยมมากขึ้นกว่านั้นและตัวอย่างเช่นการ${x:-hello world}ทำงานเกินไป

  • การขยายเลขคณิตด้วย$(( ... ))แม้ว่ามันจะไม่มีประโยชน์ส่วนใหญ่อยู่ที่นั่น (แต่คุณสามารถซ้อนการทดแทนคำสั่งหรือการขยายตัวแปรได้เช่นกัน POSIX ระบุว่า :

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

    ดังนั้นพฤติกรรมนี้เป็นสิ่งจำเป็นอย่างชัดเจน นั่นหมายความว่าecho "abc $((4 "*" 5))"ทำเลขคณิตแทนที่จะเป็นแบบโค้ง

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

  • การแปลเฉพาะสถาน$"..."ที่ซึ่งจริง ๆ แล้วใช้"เป็นองค์ประกอบหลัก $"จะถือว่าเป็นหน่วยเดียว

มีอีกหนึ่งกรณีการซ้อนที่คุณอาจไม่คาดหวังไม่เกี่ยวข้องกับเครื่องหมายคำพูดซึ่งขึ้นอยู่กับการขยายรั้ง : {a,b{c,d},e}ขยายเป็น "a bc bd e" ${x:-a{b,c}d}ทำได้รัง แต่; มันจะถือว่าเป็นการทดแทนพารามิเตอร์ที่ให้ " a{b,c" ตามด้วย " d}" นั่นคือเอกสาร :

เมื่อใช้วงเล็บปีกกาวงเล็บปีกกาที่ตรงกันคือ '}' แรกที่ไม่ได้ใช้เครื่องหมายแบคสแลชหรือภายในสตริงที่ยกมาและไม่อยู่ในการขยายเลขคณิตแบบฝังการแทนคำสั่งหรือการขยายพารามิเตอร์


ตามกฎทั่วไปตัวคั่นที่มีโครงสร้างทั้งหมดจะแยกวิเคราะห์เนื้อความของตนเองโดยไม่ขึ้นกับบริบทโดยรอบ (และข้อยกเว้นจะถือเป็นข้อบกพร่อง ) ในสาระสำคัญเมื่อเห็น$(รหัสการแทนที่คำสั่งเพียงแค่ขอให้ parser กินสิ่งที่มันสามารถจากร่างกายราวกับว่ามันเป็นโปรแกรมใหม่แล้วตรวจสอบว่าเครื่องหมายสิ้นสุดที่คาดหวัง (unescaped )หรือ))หรือ}) ปรากฏขึ้นเมื่อ sub-parser ทำงาน จากสิ่งที่มันสามารถบริโภคได้

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

คุณสามารถซ้อนข้อความได้ลึกเท่าที่คุณต้องการผ่านโครงสร้างเหล่านี้และมันจะทำงานได้ตามที่คาดไว้ จะไม่มีที่ไหนสับสนได้โดยดูข้อความที่อยู่ตรงกลาง นั่นจะเป็นการเริ่มต้นของสตริงที่ยกมาใหม่ในบริบทภายใน


ขอบคุณ ใน"blah/blah\n$(cat "${tmpdir}/${filename}.jpdf")"ทำไมเป็นครั้งที่สองราคาคู่ไม่สิ้นสุดของใบเสนอราคาครั้งแรก (ตามที่แสดงโดยการเน้นไวยากรณ์ในการตอบของคุณ) แต่จุดเริ่มต้นของภายในสตริง$(...)? เป็นเพราะ parser ของ bash อยู่จากบนลงล่างแทนที่จะเป็น top-up หรือไม่
ทิม

2
มีจำนวนมากของการเปลี่ยนแปลงในการจัดการคือ"${var-"foo"}"( echo "${-+"*"}"เป็นเช่นเดียวกับecho *ในบอร์นหรือเปลือกกรเป็นต้น) และพฤติกรรมที่จะทำไม่ได้ระบุอย่างชัดเจนในรุ่นถัดไปของมาตรฐาน ดูการสนทนาได้ที่mail-archive.com/austin-group-l@opengroup.org/msg00167.html
Stéphane Chazelas

3

บางทีการดูทั้งสองตัวอย่างด้วยprintf(แทนที่จะเป็นecho) จะช่วย:

$ printf '<%s> ' "(echo " * ")"; echo
<(echo > <test.txt> <ppcg.sh> <file1> <file2> <file3> <)>

มันพิมพ์(echo (คำแรกรวมทั้งพื้นที่ต่อท้าย) )ไฟล์บางและคำพูดปิด วงเล็บเป็นเพียงส่วนหนึ่งของสตริงที่ยกมา
เครื่องหมายดอกจัน (ไม่ได้อ้างอิงตอนนี้เนื่องจากมีการจับคู่อัญประกาศคู่กัน) จะขยายเป็น glob ไปยังรายการของไฟล์ที่ตรงกัน จากนั้นวงเล็บปิด(echo 

อย่างไรก็ตามคำสั่งที่สองของคุณทำงานดังนี้:

$ printf '<%s> ' "$(echo " * ")" ; echo
< * >

การ$เริ่มต้นการทดแทนคำสั่ง นั่นเป็นการเริ่มต้นข้อความใหม่
เครื่องหมายดอกจันถูกยกมา" * "และนั่นคือสิ่งที่คำสั่ง (นี่คือคำสั่งและไม่ใช่สตริงที่ยกมา) echoเอาท์พุท ในที่สุดprintfอีกรูปแบบและพิมพ์เป็น*< * >

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