การขยายตัวของตัวแปรภายในเครื่องหมายคำพูดเดี่ยวในคำสั่งใน Bash


393

ฉันต้องการเรียกใช้คำสั่งจากสคริปต์ทุบตีซึ่งมีคำพูดเดียวและคำสั่งอื่น ๆ ที่อยู่ในคำพูดเดียวและตัวแปร

เช่น repo forall -c '....$variable'

ในรูปแบบนี้$จะหนีออกมาและตัวแปรจะไม่ขยาย

ฉันลองใช้รูปแบบต่อไปนี้ แต่ถูกปฏิเสธ:

repo forall -c '...."$variable" '

repo forall -c " '....$variable' "

" repo forall -c '....$variable' "

repo forall -c "'" ....$variable "'"

ถ้าฉันแทนค่าแทนที่ตัวแปรคำสั่งจะถูกดำเนินการได้ดี

โปรดบอกฉันว่าฉันจะไปผิดที่


33
repo forall -c ' ...before... '"$variable"' ...after...'
n คำสรรพนาม 'm

2
@nm เครื่องหมายคำพูดเดี่ยวเป็นส่วนหนึ่งของคำสั่ง repo ฉันไม่คิดว่ามันจะใช้ได้
Rachit

1
bashกินคำพูดเดียว ไม่ว่าคุณจะไม่อยู่bashหรือเครื่องหมายคำพูดเดี่ยวไม่ได้เป็นส่วนหนึ่งของrepoคำสั่ง
n คำสรรพนาม 'm

ไม่แน่ใจว่าฉันได้รับคุณ แต่ในกรณีที่คำพูดเดียวต้องอยู่ที่นั่นจะไม่ repo forall -c "'... ก่อน ... " $ แปร "... หลังจาก ...
ecv

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

คำตอบ:


621

ภายในเครื่องหมายคำพูดเดี่ยวทุกอย่างถูกเก็บรักษาไว้อย่างแท้จริงโดยไม่มีข้อยกเว้น

นั่นหมายความว่าคุณต้องปิดคำพูดใส่อะไรแล้วใส่ใหม่อีกครั้ง

'before'"$variable"'after'
'before'"'"'after'
'before'\''after'

การต่อเรียงคำทำได้ง่าย ๆ โดยการตีข่าว ในขณะที่คุณสามารถตรวจสอบแต่ละบรรทัดข้างต้นเป็นคำเดียวกับเชลล์ เครื่องหมายคำพูด (คำพูดเดี่ยวหรือสองคำขึ้นอยู่กับสถานการณ์) อย่าแยกคำ ใช้เพื่อปิดการแปลความหมายของอักขระพิเศษต่าง ๆ เช่นช่องว่าง$, ;... เพื่อการสอนที่ดีเกี่ยวกับการอ้างอิงดูคำตอบของ Mark Reed ที่เกี่ยวข้องเพิ่มเติม: ตัวละครใดที่ต้องหลบหนีด้วยการทุบตี?

อย่าต่อเชื่อมสตริงที่ตีความโดยเชลล์

คุณควรหลีกเลี่ยงการสร้างคำสั่งเชลล์โดยการเชื่อมตัวแปรเข้าด้วยกัน นี่เป็นความคิดที่ไม่ดีเหมือนกับการต่อเชื่อม SQL fragments (การฉีด SQL!)

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

ตัวอย่างเช่นต่อไปนี้ไม่ปลอดภัยมาก อย่าทำอย่างนี้

script="echo \"Argument 1 is: $myvar\""
/bin/sh -c "$script"

หากเนื้อหาของ$myvarไม่น่าเชื่อถือนี่คือการหาประโยชน์:

myvar='foo"; echo "you were hacked'

แทนที่จะเรียกใช้ข้างต้นให้ใช้อาร์กิวเมนต์ตำแหน่ง การเรียกใช้ต่อไปนี้ดีกว่า - ไม่ได้ใช้ประโยชน์:

script='echo "arg 1 is: $1"'
/bin/sh -c "$script" -- "$myvar"

สังเกตการใช้งานเห็บเดี่ยวในการมอบหมายให้scriptซึ่งหมายถึงการทำเครื่องหมายอย่างแท้จริงโดยไม่ต้องมีการขยายตัวแปรหรือการตีความรูปแบบอื่นใด


@ Jo.SO ว่าจะไม่ทำงาน เนื่องจากคำสั่ง repo จะใช้พารามิเตอร์เฉพาะจนกว่าเครื่องหมายคำพูดแรกจะถูกปิด สิ่งใดหลังจากนั้นจะถูกละเว้นสำหรับ repo และสร้างข้อผิดพลาดอื่น
Rachit

8
@Rachit - เชลล์ใช้งานไม่ได้ "some"thing' like'thisคือทั้งหมดหนึ่งคำไปยังเชลล์ เครื่องหมายคำพูดไม่ยุติอะไรเท่าที่แยกเป็นห่วงและมีวิธีสำหรับคำสั่งที่เรียกว่าไม่มีโดยเปลือกจะบอกสิ่งที่ถูกยกมาว่า
Mark Reed

3
ประโยคที่สองนั้น ("คุณไม่สามารถหลบหนีแม้แต่คำพูดเดียว") ทำให้คำพูดเดี่ยวเป็นหลุมดำของเชลล์

@Evert: ไม่รู้จะพูดยังไงดีกว่า ฉันถอดประโยคออกแล้ว
Jo So

@ โจโซนั่นเป็นความอัปยศ: ไม่มีเรื่องตลกที่แบน

96

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

repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff'

คำอธิบายจะตามมาหากคุณสนใจ

เมื่อคุณเรียกใช้คำสั่งจากเชลล์สิ่งที่คำสั่งนั้นรับเป็นอาร์กิวเมนต์คืออาร์เรย์ของสตริงที่สิ้นสุดด้วยค่า null สตริงเหล่านั้นอาจมีอักขระที่ไม่มีค่า null ใด ๆ

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

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

echo \"Thank\ you.\ \ That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier

... มันน่าเบื่อ ดังนั้นเชลล์เสนอทางเลือก: เครื่องหมายคำพูด เหล่านี้มาในสองสายพันธุ์หลัก

เครื่องหมายอัญประกาศคู่ถูกเรียกว่า "การจัดกลุ่มคำพูด" พวกเขาป้องกันไม่ให้ขยายสัญลักษณ์และชื่อแทน แต่ส่วนใหญ่พวกเขากำลังรวมถึงช่องว่างในคำ สิ่งอื่น ๆ เช่นการขยายพารามิเตอร์และคำสั่ง (ประเภทของสิ่งที่ส่งสัญญาณโดย$) ยังคงเกิดขึ้น และแน่นอนถ้าคุณต้องการให้การอ้างอิงสองตัวอักษรในเครื่องหมายคำพูดคู่คุณต้องแบ็กสแลช:

echo "\"Thank you. That'll be \$4.96, please,\" said the cashier"

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

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

echo '"Thank you. That'\''ll be $4.96, please," said the cashier'

ดังนั้นง่ายกว่า - มีแบ็กสแลชน้อยลงมากถึงแม้ว่าการอ้างถึงแบบโคลสอัพเดี่ยว, แบ็กสแลช - ตัวอักษร - ตัวอักษรเดียว, การอ้างถึงแบบเปิดเดี่ยวจะใช้เวลาบ้าง

Modern shells ได้เพิ่มสไตล์การอ้างอิงอื่นที่ไม่ได้ระบุโดยมาตรฐาน POSIX ซึ่งเครื่องหมายอัญประกาศเดี่ยวนำหน้าถูกนำหน้าด้วยเครื่องหมายดอลลาร์ สตริงที่ยกมาดังนั้นตามอนุสัญญาที่คล้ายกันกับตัวอักษรสตริงในภาษาการเขียนโปรแกรม ANSI C และบางครั้งจึงเรียกว่า "สตริง ANSI" และ$'... 'คู่ "ANSI คำพูด" ภายในสตริงดังกล่าวคำแนะนำข้างต้นเกี่ยวกับแบ็กสแลชที่ถูกถ่ายจะไม่มีผลบังคับใช้อีกต่อไป พวกมันกลับมาเป็นพิเศษอีกครั้ง - ไม่เพียง แต่คุณสามารถใส่เครื่องหมายคำพูดเดี่ยวหรือเครื่องหมายแบ็กสแลชได้โดยเตรียมแบ็กสแลชไว้ล่วงหน้า แต่เชลล์ยังขยายการเว้นอักขระ ANSI C (เช่นขึ้น\nบรรทัดใหม่\tสำหรับแท็บและ\xHHอักขระด้วย รหัสเลขฐานสิบหกHH) อย่างไรก็ตามมิฉะนั้นจะทำงานเป็นสตริงที่มีการเสนอราคาเดี่ยว: ไม่มีการทดแทนพารามิเตอร์หรือคำสั่งเกิดขึ้น:

echo $'"Thank you.  That\'ll be $4.96, please," said the cashier'

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


2
ใช่. ฉันเข้าใจแล้ว. มันทำงานได้อย่างสมบูรณ์ ขอบคุณมาก ๆ สำหรับความช่วยเหลือของคุณ!
Rachit

6
คำตอบนี้ยอดเยี่ยมมาก
ViníciusFerrão

1
เป็น$'string'รูปแบบตาม POSIX? นอกจากนี้ยังมีชื่อสำหรับมันหรือไม่?
สัญลักษณ์แทน

2
@Wildcard $'string'เป็นส่วนขยายที่ไม่ใช่ POSIX ซึ่งฉันเคยเห็นว่าเป็น "ANSI strings" ฉันได้รวมข้อเท็จจริงเหล่านั้นไว้ในคำตอบแล้ว สตริงดังกล่าวทำงานในเชลล์ที่เข้ากันได้กับ Bourne ที่ทันสมัยที่สุด: bash, dash, ksh (ทั้ง AT&T และ PD) และ zsh ล้วนสนับสนุนพวกมัน
Mark Reed


3

ด้านล่างคือสิ่งที่ใช้ได้ผลสำหรับฉัน -

QUOTE="'"
hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"

2
ฉันหวังว่า TBL_HDFS_DIR_PATH ไม่ใช่ผู้ใช้ที่ป้อนอินพุต btw นี่จะอนุญาตให้มีการฉีด sql ที่ดี ...
domenukk

1
การใส่QUOTEตัวแปรแยกต่างหากนั้นไม่จำเป็นเลย เครื่องหมายคำพูดเดี่ยวในเครื่องหมายคำพูดคู่เป็นเพียงอักขระปกติ (และอย่าใช้ตัวพิมพ์ใหญ่สำหรับตัวแปรส่วนตัวของคุณ)
tripleee

2

แก้ไข: (ตามความคิดเห็นในคำถาม :)

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


ขอบคุณเควินสำหรับความช่วยเหลือของคุณ
Rachit

2

เพียงใช้ printf

แทน

repo forall -c '....$variable'

ใช้ printf เพื่อแทนที่โทเค็นของตัวแปรด้วยตัวแปรที่ขยาย

ตัวอย่างเช่น:

template='.... %s'

repo forall -c $(printf "${template}" "${variable}")

สิ่งนี้แตกสลาย printfไม่ได้ซื้ออะไรให้คุณที่นี่จริง ๆ และไม่สามารถอ้างอิงการชดเชยคำสั่งทำให้เกิดปัญหาการอ้างอิงใหม่
tripleee

0

ตัวแปรสามารถมีคำพูดเดียว

myvar=\'....$variable\'

repo forall -c $myvar

สามารถทำได้ แต่นี่ไม่ใช่วิธีที่ถูกต้องที่จะทำคุณควรใส่"$myvar"เครื่องหมายคำพูดคู่
tripleee

-2

มันใช้งานได้สำหรับคุณหรือไม่

eval repo forall -c '....$variable'

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