ค่าชั่วคราวของสคริปต์ bash ในคำสั่ง


11

ชอบคำสั่งด้านล่าง

if true; then
   IFS=":" read a b c d e f <<< "$test"

หนังสือบอกว่าเมื่อใช้คำสั่งการกำหนดค่า ( IFS ":") ก่อนคำสั่งหลัก ( read a b c d e f <<< "$value") ค่าของมันจะมีผลกับคำสั่งหลักชั่วคราว ดังนั้นการใช้คำสั่งคั่นread:

แต่เช่นเดียวกับคำสั่งนี้

if true; then
   HOME="hello" echo "$HOME"

ข้อความสะท้อนไม่ได้เป็นสวัสดี ความหมายที่แท้จริงของคำสั่งข้างต้นคืออะไร?

คำตอบ:


5

นี่เป็นคำถามเกี่ยวกับการประเมินผลงาน ตัวอย่างทั้งสองทำงานในลักษณะเดียวกันปัญหาเกิดขึ้นเนื่องจากเชลล์ (bash, here) ขยายตัวแปร

เมื่อคุณเขียนคำสั่งนี้:

HOME="foo" echo $HOME

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

เพื่ออธิบายให้พิจารณาสิ่งนี้:

$ HOME="foo" bash -c 'echo $HOME'
foo
$ echo $HOME
/home/terdon

ดังที่คุณเห็นด้านบนคำสั่งแรกจะพิมพ์ค่าการเปลี่ยนแปลงชั่วคราวของHOMEและคำสั่งที่สองจะพิมพ์ต้นฉบับซึ่งแสดงให้เห็นว่าตัวแปรนั้นถูกเปลี่ยนแปลงชั่วคราวเท่านั้น เนื่องจากbash -c ...คำสั่งอยู่ในเครื่องหมายอัญประกาศเดี่ยว ( ' ') แทนที่จะเป็น double double ( " ") ตัวแปรจึงไม่ถูกขยายและถูกส่งผ่านตาม - ไปยังกระบวนการทุบตีใหม่ กระบวนการใหม่นี้จะขยายและพิมพ์ค่าใหม่ที่ตั้งไว้ คุณสามารถเห็นสิ่งนี้เกิดขึ้นถ้าคุณใช้set -x:

$ set -x
$ HOME="hello" echo "$HOME"
+ HOME=hello         
+ echo hello
hello

ที่คุณสามารถดูข้างต้นตัวแปร จะไม่ผ่านไป$HOME echoมันเห็นค่าที่ขยายออกเท่านั้น เปรียบเทียบกับ:

$ HOME="hello" bash -c 'echo $HOME'
+ HOME=hello
+ bash -c 'echo $HOME'
hello

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


7

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

สมมติ test=1:2:3:4:5:6

ลองดูคำสั่งนี้: IFS=":" read a b c d e f <<< "$test"

หลังจาก tokenizing และการขยายพารามิเตอร์เกิดขึ้น:IFS=":" read a b c d e f <<< "1:2:3:4:5:6"

เชลล์จะตั้งค่าตัวแปร IFS สำหรับช่วงเวลาของคำสั่ง read และreadรู้วิธีใช้ $ IFS กับอินพุตและให้ค่ากับชื่อตัวแปร

คำสั่งนี้มีเรื่องราวที่คล้ายกัน แต่ผลลัพธ์ที่แตกต่าง: HOME="hello" echo "$HOME"

เนื่องจากการขยายพารามิเตอร์เกิดขึ้นก่อนที่คำสั่งจะเริ่มเชลล์มี:

HOME="hello" echo "/home/username"

จากนั้นในระหว่างการดำเนินการคำสั่ง echo ค่าใหม่ของ $ HOME จะไม่ถูกใช้เลย

เพื่อให้บรรลุสิ่งที่คุณพยายามจะทำให้เลือกข้อใดข้อหนึ่ง

# Delay expansion of the variable until its new value is set
HOME="hello" eval 'echo "$HOME"'

หรือ

# Using a subshell, so the altered env variable does not affect the parent.
# The semicolon means that the variable assignment will occur before
# the variable expansion
(HOME="hello"; echo "$HOME")

แต่อย่าเลือกอันแรก


น่าจะดีกว่าถ้าเลือกอันแรก อย่างน้อยก็เร็วกว่ามาก เมื่อ eval คือคำตอบบางครั้งคุณอาจถามคำถามผิด ๆ แต่ถ้าใครบางคนต้องทำอย่างนั้นด้วยเหตุผลบางอย่างการเปลี่ยนคำตอบไม่ได้ทำให้คำถามนั้นผิดไป localทางออกก็คือการห่อไว้ในฟังก์ชั่นและการใช้งาน
23013

ทำไมจึงควรevalหลีกเลี่ยงการแก้ปัญหา?
DarkHeart

หากคุณไม่ได้ควบคุมการป้อนข้อมูลอย่างเคร่งครัดคุณจะต้องระมัดระวังเกี่ยวกับโค้ดที่อนุญาตให้บุคคลอื่นฉีดเข้าไปในโปรแกรมของคุณ
เกล็นแจ็คแมน

-1

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

โดยนัยenv(ในตัวอย่างของคุณ) แก้ไขสภาพแวดล้อมในขณะที่echo ...ใช้คนในท้องถิ่น - ดังนั้นจึงenvไม่มีผลกระทบ

หากต้องการปรับเปลี่ยนตัวแปรโลคัลให้ใช้คำสั่ง

( HOME="foo" ; echo "$HOME" )

นี่คือวงเล็บกำหนดขอบเขตของการมอบหมายนี้


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