ทำไม“ A = 10 echo $ A” ไม่พิมพ์ 10


25

คำสั่งนี้:

A=10 echo $A

พิมพ์บรรทัดว่าง ทำไมไม่10? ทำไมการตั้งค่าสภาพแวดล้อมชั่วคราวในสถานที่ไม่ทำงาน

ฉันต้องการทราบเหตุผลและคำอธิบายมากกว่าวิธีการแก้ปัญหา

ฉันใช้

LANG=C gcc ...

เพื่อบังคับให้ gcc ใช้ภาษาทางเลือก (อังกฤษ) แทนภาษาของระบบ (ภาษาจีน) ดังนั้นฉันคิดว่าVAR=valueคำนำหน้าจะติดตั้งสภาพแวดล้อมชั่วคราวสำหรับคำสั่งต่อไปนี้ แต่ดูเหมือนว่าฉันมีความเข้าใจผิดบ้าง

คำตอบ:


22

เป็นเรื่องของลำดับที่ขั้นตอนต่าง ๆ ของการประเมินคำสั่งเกิดขึ้น

A=10 echo $Aครั้งแรกจะแยกวิเคราะห์คำสั่งเป็นคำสั่งง่ายๆที่ทำจากสามคำA=10, และecho $Aจากนั้นแต่ละคำจะผ่านการแทนที่ตัวแปรเช่นการแปลงการขยายตัวแปรเช่นเป็น$Aค่าของพวกเขา (ฉันไม่ทำตามขั้นตอนที่ไม่สามารถมองเห็นได้)

หากAมีค่าfooแรกผลมาจากขั้นตอนการขยายตัวที่เป็นคำสั่งง่ายๆซึ่งยังคงมีคำสามคำ: A=10, และecho foo(เชลล์ยังจำได้ในตอนนี้ว่าอักขระตัวใดที่อยู่ในเครื่องหมายคำพูด - ในกรณีนี้ไม่มี) ขั้นตอนต่อไปคือการเรียกใช้คำสั่ง ตั้งแต่A=10เริ่มต้นด้วยชื่อตัวแปรที่ถูกต้องตามด้วยเครื่องหมายเท่ากับจะถือว่าเป็นการมอบหมาย ตัวแปรAถูกตั้งค่าเป็น10ทั้งเชลล์และสภาพแวดล้อมระหว่างการเรียกใช้งานคำสั่ง (โดยปกติคุณต้องเขียนexport Aให้Aอยู่ในสภาพแวดล้อมและไม่ใช่แค่เป็นตัวแปรของเชลล์นี่เป็นข้อยกเว้น) คำถัดไปไม่ใช่การกำหนดค่าดังนั้นจึงถือว่าเป็นชื่อคำสั่ง (เป็นคำสั่งในตัว) echoคำสั่งไม่ขึ้นอยู่กับตัวแปรใด ๆ เพื่อให้มีตรงผลเช่นเดียวกับA=10 echo $Aecho $A

หากคุณต้องการตั้งค่าตัวแปรสำหรับช่วงเวลาของคำสั่งเท่านั้น แต่การกำหนดเข้าสู่บัญชีในขณะที่ดำเนินการคำสั่งคุณสามารถใช้ subshell เชลล์ย่อยที่ระบุโดยวงเล็บทำให้การเปลี่ยนแปลงสถานะทั้งหมด (การกำหนดตัวแปรไดเรกทอรีปัจจุบันนิยามฟังก์ชัน ฯลฯ ) ภายในกับเชลล์ย่อย

(A=10; echo $A)

ทำเช่นนั้นexport A=10หากคุณต้องการส่งออกตัวแปรไปยังสภาพแวดล้อมเพื่อให้เห็นได้จากโปรแกรมภายนอก


ขอบคุณฉันสามารถพูดA=10 (echo $A)และรับได้10ไหม
Earth Engine

2
@EarthEngine ไม่นั่นจะเป็นข้อผิดพลาดทางไวยากรณ์ การมอบหมายจะต้องอยู่ที่จุดเริ่มต้นของคำสั่งง่าย ๆ (เช่นชื่อคำสั่งและพารามิเตอร์บางอย่างและอาจมีการมอบหมายเริ่มต้นและการเปลี่ยนเส้นทางบางส่วน) A=10; (echo $A)เอาต์พุต10แต่ยังตั้งค่าAสำหรับส่วนที่เหลือของสคริปต์
Gilles 'หยุดความชั่วร้าย'

2
@ EarthEngine แต่คุณสามารถพูดA=10 eval 'echo $A'ได้ เครื่องหมายคำพูดเดี่ยวหยุด$Aแปลจนกระทั่งบรรทัดทั้งหมดได้รับการประเมิน ... ตามเวลา A = 10 ฉันพิจารณาคำตอบนี้มากกว่าคำตอบที่ยอมรับ
Oli

ฉันคิดว่านี่เป็นคำอธิบายที่ถูกต้อง สาเหตุของพฤติกรรมเป็นเพียงลำดับเมื่อการขยายตัว$AและการมอบหมายAเกิดขึ้น Eg A=5; A=6 let 'a=A'; echo $aส่งคืน6ไม่ได้5และฉันไม่คิดว่าจะletเริ่ม subshell เนื่องจากเป็นคำสั่งในตัว
David Ongaro

@EarthEngine: เมื่อมันบอกว่าคำอธิบายที่ถูกต้องเป็นคำสั่งของการประเมินผลก็อาจจะทำให้เข้าใจผิด: A=10 echo $Aจะไม่ได้ตั้งค่าA=10สำหรับคำสั่งใด ๆ หลังจากนั้นถึงแม้ว่าพวกเขาจะอยู่ในสายที่แตกต่างกัน (เมื่อเห็นได้ชัดว่าได้รับมอบหมายได้รับการประเมินแล้ว) มันไม่เกี่ยวกับการสั่งซื้อมันเกี่ยวกับขอบเขต
MestreLion

37

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

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

นั่นเป็นสาเหตุที่A=10 echo $Aไม่ทำงานตามที่คาดไว้: A=10จะถูกตั้งค่าสำหรับ echo แต่ echo จะไม่สนใจค่าของตัวแปรสภาพแวดล้อมAภายใน และ$Aถูกแทนที่ด้วยค่าที่ตั้งไว้ในเปลือกปัจจุบัน (ซึ่งก็คือไม่มี) แล้วส่งผ่านเป็นอาร์กิวเมนต์เพื่อสะท้อน

ดังนั้นสมมติฐานของคุณถูกต้อง: VAR=value command ใช้งานได้ แต่จะมีความเกี่ยวข้องเฉพาะถ้าใช้commandภายในVAR หากไม่ใช่คุณยังคงสามารถผ่านเป็นอาร์กิวเมนต์ไปได้ แต่อาร์กิวเมนต์จะถูกแทนที่ด้วยเชลล์ปัจจุบันดังนั้นจึงต้องตั้งค่าก่อนใช้งาน:valuecommandVAR=value; command "$VAR"

หากคุณรู้วิธีสร้างสคริปต์ที่เรียกใช้งานได้คุณสามารถลองใช้วิธีทดสอบนี้ได้:

#!/bin/sh
echo "1st argument is $1"
echo "A is $A"

บันทึกเป็นtestscriptและลอง:

$ A=5; A=10 testscript "$A"; echo "$A"
1st argument is 5
A is 10
5

สุดท้าย แต่ไม่น้อยก็คุ้มค่าการทราบความแตกต่างระหว่างเปลือกและสภาพแวดล้อมของตัวแปรและโปรแกรมการขัดแย้ง

นี่คือบางส่วนอ้างอิงที่ดี:

.

(*) หมายเหตุ: ในทางเทคนิคเปลือกไม่ตั้งอยู่ในสภาพแวดล้อมปัจจุบันเกินไปและนี่คือเหตุผล: คำสั่งบางอย่างเช่นecho, readและtestมีbuiltins เปลือกและเป็นเช่นพวกเขาไม่ได้วางไข่กระบวนการเด็ก พวกเขาทำงานในสภาพแวดล้อมปัจจุบัน แต่เชลล์จะดูแลการมอบหมายเท่านั้นคงอยู่จนกว่าคำสั่งจะทำงานดังนั้นสำหรับวัตถุประสงค์ในทางปฏิบัติทั้งหมดผลจะเหมือนกัน: การมอบหมายจะเห็นได้จากคำสั่งเดียว


2
คำอธิบายนี้ไม่ถูกต้องจริงแม้ว่าจะส่งผลให้ข้อสรุปที่ถูกต้องในทั้งหมด แต่บางกรณีมุม คำอธิบายที่แท้จริงคือลำดับของการขยาย: $Aมีการประเมินก่อนที่จะมีการมอบหมาย ฉันคิดว่าคำอธิบายของคุณจะล้มเหลวในกรณีของยูทิลิตี้ในตัวปกติซึ่งพฤติกรรมขึ้นอยู่กับค่าของตัวแปร: บิวด์อินจะเห็นค่าที่กำหนด ตัวอย่างทั่วไปคือIFS=: read one two three restซึ่งอ่านฟิลด์ที่คั่นด้วยโคลอน: readบิลด์อินจะเห็นค่าIFSเป็น
Gilles 'ดังนั้น - หยุดความชั่วร้าย'

“ ไม่ใช่สำหรับเชลล์ปัจจุบันเอง” ผิด: ตัวแปรถูกตั้งค่าไว้ในเชลล์ปัจจุบัน แต่จะอยู่ได้นานสำหรับคำสั่งง่าย ๆ ปัจจุบันเท่านั้น echoจะเห็นคุณค่า10สำหรับAถ้ามันได้รับการดูแล
Gilles 'หยุดชั่วร้าย'

@Gilles: ขอบคุณมากสำหรับการชี้แจง! ฉันไม่ได้ตระหนักถึงความละเอียดอ่อนนี้ ดังนั้นถ้าผมเข้าใจอย่างถูกต้องทุบตีมีชุดสำหรับในปัจจุบันสภาพแวดล้อมอย่างอื่น builtins (ซึ่งไม่ได้วางไข่ใหม่pid) จะไม่เห็นได้รับมอบหมายอื่น ๆ คำสั่ง "regurlar" จะ แต่มันunsetsหลังจากที่คำสั่งจะทำเพื่อ จำกัด ขอบเขตที่ได้รับมอบหมาย ถูกต้องหรือไม่ฉันจะแก้ไขคำตอบของฉันให้เหมาะสม PS: ด้านเทคนิคกันฉันยังคงคิดว่าคำตอบควรเน้นไปที่ขอบเขตไม่ใช่ลำดับการประเมินผลมิฉะนั้นใครจะคิดว่าA=10 test; echo $Aจะพิมพ์ 10
MestreLion

3

วิธีหนึ่งที่อาจสะอาดในการทำสิ่งที่คุณต้องการคือการออกคำสั่ง:

A=10 eval 'echo $A'

ซึ่งจะมีผลทำให้การแทนที่ค่า 10 เข้าแทนที่ $ A เป็นบริบทในภายหลัง (เช่น 'Inside' eval ซึ่งรู้อยู่แล้วเกี่ยวกับการมอบหมาย) โปรดทราบว่าคำพูดเดียวมีความสำคัญ โครงสร้างดังกล่าวสื่อสารการกำหนดให้กับคำสั่งที่คุณต้องการอย่างชัดเจน (echo ในกรณีนี้) โดยไม่เสี่ยงต่อการก่อให้เกิดมลพิษในสภาพแวดล้อมปัจจุบันของคุณ

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