เหตุใดการขยายตัวตัวแปร bash จึงเก็บคำพูดไว้


12
> echo "hi"
hi
> VAR='echo "hi"'
> $VAR
"hi"

ทำไมเอาต์พุตของคำสั่งด้านบนจึงแตกต่างกัน?

สิ่งที่คล้ายกันเกิดขึ้นกับคำพูดเดียว:

> VAR="echo 'hi'"
> $VAR
> 'hi'

6
โปรดอย่าติดนิสัยในการฝังสนิปเพตของสคริปต์ในตัวแปร สิ่งนี้มีแนวโน้มที่จะเป็นเรื่องที่ดีที่สุดและevalเป็นช่องโหว่ของความปลอดภัยที่อาจเกิดขึ้นซึ่งคุณต้องเหยียบอย่างระมัดระวัง
jw013

@ jw013 จุดดีและบทความที่ดี ฉันชอบคำพูด "ตัวแปรเก็บข้อมูลฟังก์ชั่นถือรหัส" จากลิงก์แรก แต่สำหรับการใช้งานของฉันข้อมูลที่มอบให้กับฟังก์ชั่น (ในกรณีนี้at) คือรหัส เคล็ดลับใด ๆ เกี่ยวกับวิธีที่ปลอดภัยในการจัดระเบียบ / รหัสเก็บว่าจะได้รับat?
Cory Klein

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

ขออภัยฉันไม่ต้องการเบี่ยงเบนความสนใจด้วยรายละเอียดมากเกินไป แต่สิ่งที่ฉันทำไม่ซับซ้อนจริงๆ IMO ฉันกำลังสร้างสคริปต์ที่ใช้เวลา "เวลา" และ "ข้อความ" จากนั้นก็วิ่งatสำหรับให้ "เวลา" และบอกว่าจะเรียกใช้คำสั่ง at ใช้ "ข้อความ" จาก stdin และยังใช้พารามิเตอร์แบบคงที่อื่น ๆ ปัญหาคือฉันต้องไพพ์พารามิเตอร์ "ข้อความ" จากผู้ใช้ลงในคำสั่ง แต่ฉันไม่ได้ใช้งานตัวเองจริง ๆฉันกำลังบอกให้ทำ dzen2dzen2dzen2dzen2at
Cory Klein

คำตอบ:


16

คู่ราคาพิเศษจะถูกใช้โดยขั้นตอนการประเมินพิเศษเท่านั้น ตัวอย่างเช่นบังคับโดยeval:

bash-4.2$ VAR='echo "hi"'

bash-4.2$ $VAR
"hi"

bash-4.2$ eval $VAR
hi

แต่โดยทั่วไปเป็นความคิดที่ดีที่จะใส่คำสั่งพร้อมพารามิเตอร์ในหนึ่งสตริง ใช้อาร์เรย์แทน:

bash-4.2$ VAR=(echo "hi")

bash-4.2$ "${VAR[@]}"
hi

1
สิ่งสำคัญคือต้องทราบว่าคำพูดมีการประเมินต่างกัน เครื่องหมายอัญประกาศคู่ (") อนุญาตให้ประเมินสตริงที่ล้อมรอบเครื่องหมายคำพูดเดี่ยว (') พิมพ์สตริงเป็นตัวอักษรตัวอย่าง: "$(ls)"และ'$(ls)'นี่คือเหตุผลที่เครื่องหมายคำพูดปรากฏในตัวอย่างคำถามต้นฉบับ
Joseph Kern

อาร์เรย์ยังเป็นต้นเหตุของปัญหา รหัสเป็นของฟังก์ชั่นข้อมูลไปยังตัวแปร ตัวอย่างที่คุณนำเสนอใช้งานได้เพียงเพราะคำพูดจะถูกลบออกในการแยกของอาร์เรย์ printf '<%s> ' "${VAR[@]}"จะแสดงให้เห็นว่าคำพูดได้ถูกลบออกไปแล้ว หากคุณตั้งค่า VAR ว่าVAR=(echo \"hi\")มีคำพูดจริงปัญหาเดียวกันจะปรากฏขึ้นอีกครั้ง$ ${VAR[@]}จะพิมพ์"hi"

9

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


2

หากคุณย้อนกลับไปเล็กน้อยคุณจะเห็นว่าทำไมการแทนที่ตัวแปรจึงควรเก็บราคาไว้

จุดอ้างอิงในเชลล์ Unix / Linux / BSD คือการเก็บชิ้นส่วนของสตริงไว้ด้วยกัน เนื่องจากโดยค่าเริ่มต้นเชลล์ใช้ช่องว่างเป็นตัวคั่นโทเค็นสตริงที่มีช่องว่าง (เช่น "หนึ่งสองสาม") หากไม่ยกมาหรือหนีออกมาอย่างใดจะได้รับการแยกวิเคราะห์เป็น 3 สตริง: "หนึ่ง", "สอง" และ "สาม"

หากโปรแกรมเมอร์ต้องการสตริงที่มีค่าของตัวแปรที่ถูกสอดแทรก:

VAR=two
STRING="one $VAR three"

เชลล์ไม่ควรลบเครื่องหมายคำพูดอย่างแน่นอน: สตริงที่มีช่องว่างจะแยกวิเคราะห์เป็น 3 สตริงที่เล็กกว่า

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