ฉันจะตั้งค่าตัวแปรสภาพแวดล้อมในบรรทัดคำสั่งและให้มันปรากฏในคำสั่งได้อย่างไร


152

ถ้าฉันวิ่ง

export TEST=foo
echo $TEST

มันส่งผลฟู

ถ้าฉันวิ่ง

TEST=foo echo $TEST

มันไม่ใช่. ฉันจะรับฟังก์ชั่นนี้โดยไม่ใช้การส่งออกหรือสคริปต์ได้อย่างไร


ระวังเรื่องนี้มีมากกว่าเรื่องที่ปรากฏในตอนแรก ฉันเชิญคุณตรวจสอบคำตอบของฉัน
jasonleonhard

คำตอบ:


180

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

TEST=foo; echo $TEST

มันจะทำงาน.

exportจะทำให้ตัวแปรปรากฏในสภาพแวดล้อมของคำสั่งที่ถูกเรียกใช้งานในภายหลัง (สำหรับวิธีการทำงานของ bash see help export) หากคุณต้องการให้ตัวแปรปรากฏในสภาพแวดล้อมของคำสั่งเดียวให้ใช้สิ่งที่คุณได้ลองแล้วเช่น:

TEST=foo your-application

1
แล้ว / usr / bin / env ล่ะ? ที่ไม่ทำงานอย่างใดอย่างหนึ่งคุณรู้ว่าทำไม
Benubird

5
เหตุผลเดียวกัน - เชลล์ขยาย$TESTก่อนที่จะดำเนินการบรรทัดคำสั่ง เมื่อechoมีการทำงาน (ยังทราบว่าechoโดยปกติจะแปลเป็นคำสั่งในตัวเชลล์และไม่ให้/bin/echo) มันจะเห็นการตั้งค่าตัวแปรในสภาพแวดล้อมของมัน อย่างไรก็ตามecho $TESTอย่าบอกechoให้เอาท์พุทเนื้อหาของตัวแปรTESTจากสภาพแวดล้อมของมัน มันบอกให้เชลล์ทำงานechoด้วยการโต้แย้งว่าอะไรก็ตามที่อยู่ในตัวแปรที่เรียกว่าTEST- และสิ่งเหล่านี้เป็นสองสิ่งที่แตกต่างกันมาก
เตอร์

@peterph หากเชลล์ขยายตัวแปรในบรรทัดคำสั่งก่อนที่มันจะรันคำสั่งจริงๆแล้วทำไมมันไม่ขยายมันvar=value sh -c 'echo "$var"'?
haccks

ดังที่ฉันได้อธิบายให้คุณทราบที่นี่ : เป็นเพราะตัวแปรถูกขยายในเครื่องหมายคำพูดคู่ (เช่น"… $var …") แต่ไม่ใช่ในเครื่องหมายคำพูดเดี่ยว (เช่น'… $var …') เนื่องจากecho "$var"อยู่ในเครื่องหมายคำพูดเดี่ยวสตริงทั้งหมดจึงถูกส่งผ่านไปยังsh -cเชลล์( ) ใหม่โดยไม่ต้องตีความโดยเชลล์เชิงโต้ตอบภายนอก … (ต่อ)
G-Man

(ต่อ) …ตามที่igal พูดเมื่อวานมีการแยกวิเคราะห์สองรอบ - แม้ว่าฉันเชื่อว่า igal จะอ่านคำถามของคุณผิด ไม่มีการแยกวิเคราะห์สองรอบนอกเหนือจากการวิเคราะห์คำที่ทำโดยเชลล์พาเรนต์ การแยกวิเคราะห์มีสองรอบ - หนึ่งรอบทำโดยเปลือกนอก, เชิงโต้ตอบ (หลัก) และหนึ่งโดยsh -cเปลือกใหม่ ( )
G-Man

39

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

ใน

var=value echo whatever

คุณกำลังส่งผ่านvar=valueสตริงไปยังสภาพแวดล้อมที่เสียงสะท้อนได้รับ แต่echoไม่ได้ทำอะไรกับรายการสภาพแวดล้อมและการอยู่แล้วในเปลือกหอยส่วนใหญ่echoถูกสร้างขึ้นในและดังนั้นจึงไม่ดำเนินการ

ถ้าคุณเขียน

var=value sh -c 'echo "$var"'

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

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

พกพาได้ (Bourne และ POSIX):

(var=value; echo "1: $var"); echo "2: $var"

(... ) ด้านบนเริ่ม sub-shell (กระบวนการเชลล์ใหม่ในเชลล์ส่วนใหญ่) ดังนั้นตัวแปรใด ๆ ที่ประกาศว่าจะมีผลกับ sub-shell นั้นเท่านั้นดังนั้นฉันคาดว่าโค้ดด้านบนจะแสดงผลลัพธ์ "1: value" และ "2:" หรือ "2: what-var-was-set-to-before"

ด้วยเชลล์คล้ายบอร์นส่วนใหญ่คุณสามารถใช้ฟังก์ชั่นและบิวด์อิน "local":

f() {
  local var
  var=value
  echo "1: $var"
}
f
echo "2: $var"

ด้วย zsh คุณสามารถใช้ฟังก์ชั่นอินไลน์:

(){ local var=value; echo "1: $var"; }; echo "2: $var"

หรือ:

function { local var=value; echo "1: $var"; }; echo "2: $var"

ด้วย bash และ zsh (แต่ไม่ใช่ ash, pdksh หรือ AT&T ksh) เคล็ดลับนี้ใช้งานได้:

var=value eval 'echo "1: $var"'; echo "2: $var"

ตัวแปรที่ทำงานในเปลือกหอยอีกไม่กี่ ( dash, mksh, yash) แต่ไม่ได้zsh(เว้นแต่ในsh/ kshจำลอง):

var=value command eval 'echo "1: $var"'; echo "2: $var"

(ใช้commandด้านหน้าบิวด์อินพิเศษ (ที่นี่eval) ในเชลล์ POSIX ลบความพิเศษของพวกเขา (ที่นี่การกำหนดตัวแปรในจากพวกเขายังคงมีผลบังคับใช้หลังจากที่พวกเขากลับมา)


สำหรับวัตถุประสงค์ของฉันนี่คือทางออกที่ถูกต้อง ฉันไม่ต้องการให้ตัวแปร 'var' สร้างมลภาวะต่อสิ่งแวดล้อมอย่างที่ทำในคำตอบที่ยอมรับ
kronenpj

6

คุณกำลังทำมันอย่างถูกต้อง แต่ไวยากรณ์ของ bash นั้นตีความง่าย: คุณอาจคิดว่าecho $TESTสาเหตุที่ทำให้เกิดechoการเรียกTESTenv var จากนั้นพิมพ์มันไม่ได้ ได้รับดังนั้น

export TEST=123

แล้วก็

TEST=456 echo $TEST

เกี่ยวข้องกับลำดับต่อไปนี้:

  1. เชลล์แยกวิเคราะห์บรรทัดคำสั่งทั้งหมดและดำเนินการแทนค่าตัวแปรทั้งหมดดังนั้นบรรทัดคำสั่งจะกลายเป็น

    TEST=456 echo 123
  2. มันสร้าง temp vars ที่ตั้งไว้ก่อนคำสั่งดังนั้นมันจะบันทึกค่าปัจจุบันของTESTและแทนที่ด้วย 456; บรรทัดคำสั่งอยู่ในขณะนี้

    echo 123
  3. มันรันคำสั่งที่เหลือซึ่งในกรณีนี้พิมพ์ 123 เพื่อ stdout (ดังนั้นคำสั่งเชลล์ที่ยังคงไม่ได้ใช้แม้แต่ค่า temp ของTEST)

  4. มันคืนค่าของ TEST

ใช้ printenv แทนเนื่องจากไม่เกี่ยวข้องกับการทดแทนตัวแปร:

>> export TEST=123
>> printenv TEST
123
>> TEST=456 printenv TEST
456
>> printenv TEST && TEST=456 printenv TEST && TEST=789 printenv TEST && printenv TEST
123
456
789
123
>>

+1 ผมพบว่าprintenvเป็นประโยชน์สำหรับการทดสอบพิสูจน์แนวคิด / (พฤติกรรมเช่นสคริปต์ไม่เมื่อเทียบกับecho)
Daryn

5

คุณสามารถทำงานนี้ได้โดยใช้:

TEST=foo && echo $TEST

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