บางครั้งผมเห็นสคริปต์ใช้ทั้งหมดของวิธีการที่แตกต่างกันเหล่านี้อ้างข้อความบางส่วน: "..."
, '...'
, และ$'...'
$"..."
เหตุใดจึงมีการใช้เครื่องหมายคำพูดต่าง ๆ มากมาย?
พวกเขามีพฤติกรรมที่แตกต่างหรือส่งผลกระทบต่อสิ่งที่ฉันสามารถทำได้ภายในพวกเขา?
บางครั้งผมเห็นสคริปต์ใช้ทั้งหมดของวิธีการที่แตกต่างกันเหล่านี้อ้างข้อความบางส่วน: "..."
, '...'
, และ$'...'
$"..."
เหตุใดจึงมีการใช้เครื่องหมายคำพูดต่าง ๆ มากมาย?
พวกเขามีพฤติกรรมที่แตกต่างหรือส่งผลกระทบต่อสิ่งที่ฉันสามารถทำได้ภายในพวกเขา?
คำตอบ:
ทั้งหมดเหล่านี้มีความหมายแตกต่างกันและคุณสามารถเขียนสิ่งต่าง ๆ ภายในพวกเขา (หรือสิ่งเดียวกันกับความหมายที่แตกต่างกัน) เครื่องหมายคำพูดประเภทต่างๆตีความลำดับการหลีกเลี่ยงที่แตกต่างกันภายใน ( \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
\nnn
printf
"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 ซึ่งคุณไม่ต้องการที่นี่)