บางครั้งผมเห็นสคริปต์ใช้ทั้งหมดของวิธีการที่แตกต่างกันเหล่านี้อ้างข้อความบางส่วน: "...", '...', และ$'...' $"..."เหตุใดจึงมีการใช้เครื่องหมายคำพูดต่าง ๆ มากมาย?
พวกเขามีพฤติกรรมที่แตกต่างหรือส่งผลกระทบต่อสิ่งที่ฉันสามารถทำได้ภายในพวกเขา?
บางครั้งผมเห็นสคริปต์ใช้ทั้งหมดของวิธีการที่แตกต่างกันเหล่านี้อ้างข้อความบางส่วน: "...", '...', และ$'...' $"..."เหตุใดจึงมีการใช้เครื่องหมายคำพูดต่าง ๆ มากมาย?
พวกเขามีพฤติกรรมที่แตกต่างหรือส่งผลกระทบต่อสิ่งที่ฉันสามารถทำได้ภายในพวกเขา?
คำตอบ:
ทั้งหมดเหล่านี้มีความหมายแตกต่างกันและคุณสามารถเขียนสิ่งต่าง ๆ ภายในพวกเขา (หรือสิ่งเดียวกันกับความหมายที่แตกต่างกัน) เครื่องหมายคำพูดประเภทต่างๆตีความลำดับการหลีกเลี่ยงที่แตกต่างกันภายใน ( \something) หรือทำหรือไม่อนุญาตการแก้ไขตัวแปร ( $something) และการขยายประเภทอื่น ๆ
ในระยะสั้น:
'...' เป็นตัวอักษรทั้งหมด"..." อนุญาตให้ทั้งตัวแปรและอักขระเครื่องหมายคำพูดฝังตัว $'...'ดำเนินการ\nยกเว้นอักขระเช่นแต่ไม่ขยายตัวแปร$"..." สำหรับการแปลภาษามนุษย์ใน Bash และ kshสิ่งที่คุณเขียนระหว่างคำพูดเดียวจะได้รับการปฏิบัติอย่างแท้จริงและไม่ได้รับการประมวลผลเลย เครื่องหมายแบ็กสแลชและเครื่องหมายดอลลาร์ไม่มีความหมายพิเศษ ซึ่งหมายความว่าคุณไม่สามารถ backslash-escape อักขระ (รวมถึงเครื่องหมายอัญประกาศเดี่ยวอื่น ๆ !) สอดแทรกตัวแปรหรือใช้คุณสมบัติเชลล์อื่น ๆ
ตัวอย่างทั้งหมดเหล่านี้ส่งผลให้สิ่งที่เขียนระหว่างคำพูด:
'hello world' => hello world
'/pkg/bin:$PATH' => /pkg/bin:$PATH
'hello\nworld' => hello\nworld
'`echo abc`' => `echo abc`
'I\'dn\'t've' => I\dn'tve
อันสุดท้ายมีความซับซ้อน - มีสองสายอักขระที่มีเครื่องหมายคำพูดเดียวทำงานร่วมกับข้อความบางส่วนที่ไม่ได้ยกมา I\คนแรกที่มี ข้อความที่ไม่มีdn\'tเครื่องหมายอัญประกาศประกอบด้วยเครื่องหมายอัญประกาศเดี่ยวที่อยู่ที่ระดับเชลล์ดังนั้นจึงไม่เริ่มต้นสตริงที่ยกมาและรวมเป็นอักขระตัวอักษร (ดังนั้นdn't) veสตริงยกสุดท้ายเป็นเพียง ทั้งหมดนั้นรวมเข้าด้วยกันเป็นคำเดียวในวิธีปกติที่เชลล์ใช้งานได้
สำนวนที่ใช้กันทั่วไปสำหรับการรวมข้อความและตัวแปรที่แท้จริงคือการรวมมันเข้าด้วยกันเช่น:
'let x="'$PATH\"
จะส่งผลให้
let x="/usr/bin:/bin"
เป็นคำเดียว (ดีกว่าเพื่ออ้างสองครั้ง$PATHเช่นกันในกรณี - ช่องว่างหรือตัวอักษรกลมในค่าตัวแปรอาจถูกประมวลผลเป็นอย่างอื่น - แต่เพื่อประโยชน์ของตัวอย่างการทำงานที่อ่านได้ฉันไม่ได้)
ภายในเครื่องหมายอัญประกาศคู่การประมวลผลส่วนขยายสองประเภทจะได้รับการประมวลผลและคุณสามารถใช้เครื่องหมายแบ็กสแลชเพื่อยกเว้นอักขระเพื่อป้องกันไม่ให้มีการประมวลผลส่วนขยายหรือส่วนขยาย
มีการขยายสองประเภทที่เกิดขึ้นภายในเครื่องหมายคำพูดคู่:
$( การขยายตัวพารามิเตอร์$abcและ${abc} , แทนคำสั่ง$(...)และการขยายตัวทางคณิตศาสตร์$((...)) );`abc`;ภายในคำพูดเครื่องหมายสามารถยับยั้งการขยายเหล่านั้นโดยวางไว้ก่อนหรือ$ `นอกจากนี้ยังสามารถหลีกเลี่ยงการปิดการอ้างอิงสองครั้งดังนั้น\"รวม"ไว้ในสตริงของคุณหรือแบ็กสแลชอื่น เครื่องหมายแบ็กสแลชอื่น ๆ จะถูกเก็บรักษาไว้อย่างแท้จริง - ไม่มีทางหนีรอดเพื่อสร้างตัวละครอื่น ๆ และจะไม่ถูกลบออก
ตัวอย่างเหล่านี้บางอย่างมีลักษณะแตกต่างไปจากเมื่อก่อนและบางตัวอย่างก็ไม่:
"hello world" => hello world
"/pkg/bin:$PATH" => /pkg/bin:/bin:/usr/bin
"hello\nworld" => hello\nworld
"hello\\nworld" => hello\nworld
"`echo abc`" => abc
"I\'dn\'t've" => I\'dn\'t've
"I'dn't've" => I'dn't've
"I\"dn\"t've" => I"dn"t've
การอ้างถึงชนิดนี้อนุญาตให้ใช้เครื่องหมายแบคสแลชแบบ C-style แต่ไม่สามารถใช้ตัวแปรหรือการแทนที่แบบฝังได้ มันเป็นเพียงชนิดของข้อความที่สนับสนุนตัวละครหนี
นี่คือส่วนขยายจาก ksh ปัจจุบันสนับสนุนใน Bash, zsh และเชลล์อื่น ๆ เช่นกัน มันยังไม่ได้เป็นส่วนหนึ่งของมาตรฐาน POSIX ดังนั้นสคริปต์แบบพกพาสูงสุดไม่สามารถใช้งานได้ แต่สคริปต์ Bash หรือ ksh นั้นฟรี
ทั้งหมดหนีเหล่านี้สามารถนำมาใช้กับความหมายของพวกเขา C: \a, \b, \f, \n, \r, \t, \vและหนีตัวอักษร\\, \', และ\" \?พวกเขายังสนับสนุนส่วนขยาย\e(อักขระหลบหนี) และใน Bash และ ksh \cx(สิ่งที่จะถูกป้อนโดย Ctrl-xเช่น\cMคืนรถ) เชลล์มีช่วงของการขยายเล็กน้อยของตัวเอง
นอกจากนี้ยังช่วยให้ตัวละครทั่วไปสี่ชนิดหนีออกมาได้:
\nnnไบต์เดียวที่มีค่าฐานแปดnnn\xHHไบต์เดียวที่มีค่าเลขฐานสิบหกHH\uHHHHตัวเข้ารหัส Unicode ที่มีดัชนีเลขฐานสิบหกคือHHHH\UHHHHHHHHตัวเข้ารหัส Unicode ที่มีดัชนีเลขฐานสิบหกคือHHHHHHHHHตัวเลขทั้งหมดเหล่านี้เป็นทางเลือกหลังจากตัวเลขแรก
$และ`ไม่มีความหมายและเก็บรักษาไว้อย่างแท้จริงดังนั้นคุณจึงไม่สามารถรวมตัวแปรที่นั่น
$'hello world' => hello world
$'/pkg/bin:$PATH' => /pkg/bin:$PATH
$'hello\nworld' => hello
world
$'`echo abc`' => `echo abc`
$'I\'dn\'t\'ve' => I'dn't've
$'\U1f574\u263A' => 🕴☺
ส่วนใหญ่หนีเหล่านี้คุณสามารถจำลองการใช้คำสั่งแม้ว่า POSIX เพียงต้องการ, , , , , , , และทำงานที่นั่น คุณสามารถใช้แทนคำสั่งการฝังราคาคู่ภายในถ้าจำเป็น:printf\\\a\b\f\n\r\t\v\nnnprintf"Path:$(printf '\t')$PATH"
นี่เป็นส่วนขยายเฉพาะ ksh- และ Bash สำหรับการแปลสตริงข้อความภาษาธรรมชาติและทำการค้นหาส่วนภายในเครื่องหมายคำพูดในแค็ตตาล็อกข้อความ จะดำเนินการขยายคำพูดสองครั้งทั้งหมดก่อน หากไม่พบสตริงในฐานข้อมูลการแปลจะใช้เป็นการแปลของตนเอง สมมติฐานในตัวคือสตริงเป็นภาษาอังกฤษ
คุณอาจไม่ต้องการใช้อันนี้ แต่ถ้าคุณเห็นมันคุณสามารถถือว่ามันเป็นอัญประกาศปกติ
จุดหนึ่งของโน้ตก็คือว่ามีไม่มีชนิดของข้อความที่ช่วยให้ทั้งการขยายตัวพารามิเตอร์ฝังและฝังตัวหนีตัวอักษร ในกรณีส่วนใหญ่ที่คุณต้องการคุณจะดีกว่า (ปลอดภัยกว่า) โดยใช้printf:
printf 'New path: \e[1m%s\e[0m' "/pkg/bin:$PATH:"
สิ่งนี้แยกส่วนที่ชัดเจนอย่างชัดเจนเพื่อหลีกเลี่ยงอักขระและค่าข้อมูล
อีกอย่างคือรูปแบบการอ้างอิงทั้งหมดเหล่านี้สร้าง "word" คำเดียวในเชลล์เว้นแต่จะใช้ $@หรือมีการขยายอาร์เรย์${x[@]}ภายในเครื่องหมายคำพูดคู่ ทั้งแบบฟอร์มคำพูดเดี่ยวเป็นหนึ่งคำเสมอและไม่เคยขยายออกไปอีก
$"..."มาจาก ksh93, เพิ่มใน bash ใน 2.0
\cX zsh( \CXหรือ\C-Xมี\cความหมายพิเศษอยู่แล้วecho)
'let x="'$PATH\"มีความผิดในรายการบริบทในเชลล์ที่นอกเหนือจากzshที่$PATHไม่ได้อ้างถึง (ดังนั้นจะต้องแยก + glob ซึ่งคุณไม่ต้องการที่นี่)