PowerShell: การตั้งค่าตัวแปรสภาพแวดล้อมสำหรับคำสั่งเดียวเท่านั้น


113

บน Linux ฉันสามารถทำได้:

$ FOO=BAR ./myscript

เพื่อเรียก "myscript" โดยมีการตั้งค่าตัวแปรสภาพแวดล้อม FOO

สิ่งที่คล้ายกันเป็นไปได้ใน PowerShell กล่าวคือโดยไม่ต้องตั้งค่าตัวแปรก่อนเรียกใช้คำสั่งแล้วยกเลิกการตั้งค่าตัวแปรอีกครั้งหรือไม่

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

$ OPTION=1 ./myscript

และ

$ ./myscript

จะมีประโยชน์มาก


ฉันเดาว่าคำถามของฉันคือทำไมคุณต้องทำสิ่งนี้? ฉันจะคิดว่ามีทางออกที่ดีกว่านี้
EBGreen

26
นั่นไม่ใช่คำถามที่มีประโยชน์ @EBGreen ความจริงที่ว่าความสามารถมีอยู่ใน UNIX เชลล์แสดงให้เห็นว่ามีประโยชน์สำหรับมัน ปิดด้านบนของหัวของฉัน: การควบคุมชื่อผู้ใช้และที่อยู่อีเมลที่คอมไพล์ใช้ในการคอมมิต ไม่มีตัวเลือกบรรทัดคำสั่งสำหรับสิ่งเหล่านี้คุณต้องตั้งค่าเป็น ~ / .gitconfig, .git / config ในแต่ละที่เก็บหรือ envars จากตัวเลือกเหล่านั้น envars ตั้งค่าได้ง่ายกว่าอย่างชัดเจนในทันที (และแทนที่ค่าในไฟล์ได้อย่างสะดวก) ดังนั้นถ้าฉันต้องการเปลี่ยนชื่อผู้เขียนของฉันสำหรับ "คอมมิตคอมมิต" ใน powershell ต้องทำอย่างไร
Mark Reed

3
ยอมรับโดยสิ้นเชิงว่าการถามว่าทำไมถึงต้องการสิ่งนี้ไม่มีจุดหมาย เป็นเรื่องปกติเหมือน borscht เมื่อดำเนินการที่บรรทัดคำสั่งบน Linux และพวกเราถูกบังคับให้ต้องทนทุกข์ทรมานกับ powershell (ฝันร้ายทางวากยสัมพันธ์ถ้าเคยมีอยู่) ต้องค้นหาคำตอบของเทคนิคที่ชัดเจนอยู่ตลอดเวลา โดยส่วนใหญ่แล้วพวกเขาไม่ได้มีอยู่ใน powershell เว้นแต่คุณจะนับการเขียนสคริปต์ยาว ๆ เพื่อทำสิ่งเล็กน้อย นับว่าฉันผิดหวังอย่างมากกับเปลือกนั้น ...
คิม


2
ขอขอบคุณสำหรับการเชื่อมโยง @FranklinYu แต่ ณ จุดนี้มันจะเป็นความหวังในรุ่นอนาคตที่ไม่ไกลเกินกว่าที่หลังจาก v7.0
mklement0

คำตอบ:


59

โดยทั่วไปควรส่งข้อมูลไปยังสคริปต์ผ่านพารามิเตอร์แทนที่จะเป็นตัวแปร global (สภาพแวดล้อม) แต่ถ้านั่นคือสิ่งที่คุณต้องทำคุณสามารถทำได้ด้วยวิธีนี้:

$env:FOO = 'BAR'; ./myscript

ตัวแปรสภาพแวดล้อม $ env: FOO สามารถลบได้ในภายหลังดังนี้:

Remove-Item Env:\FOO

2
แค่คิด: คุณไม่สามารถสร้างกระบวนการ PowerShell ใหม่ได้หรือไม่โดยส่ง scriptblock เข้าไปในพารามิเตอร์ -Command? ด้วยวิธีนี้คุณจะไม่ต้องล้างสภาพแวดล้อมในภายหลังเนื่องจากสิ่งนั้นจะถูกบรรจุไว้ในกระบวนการย่อย แม้ว่าฉันกำลังพูดกับ PowerShell MVP ดังนั้นสิ่งนี้อาจใช้ไม่ได้ :-)
Joey

2
Keith เรามี push-environmentblock และ pop-environmentblock ใน Pscx สำหรับสถานการณ์นี้อย่างแน่นอน ;-)
x0n

2
Johannes ซึ่งก็ใช้ได้เช่นกัน แต่ดูเหมือนว่าจะโกง :-) PSCX Push / Pop-EnvironmentBlock (อันที่ฉันเปลี่ยนเพื่อให้ทำงานด้วยวิธีนี้) จะใช้งานได้ แต่ไม่มีการสนับสนุนการล้างข้อมูลอัตโนมัติที่ scriptblock มี
Keith Hill

1
คุณไม่จำเป็นต้องตั้งค่าenv:fooกลับเป็นค่าเดิม (อาจไม่ได้ตั้งค่า) แทนที่จะลบออกหรือไม่?
chwarr

1
ตามที่ @Joey กล่าวไว้ถ้าคุณต้องการทำ env vars อย่างหมดจดคุณสามารถ: & {$pre = $env:foo; $env:foo = 'bar'; ./myscript; if ($pre) {$env:foo = $pre} else {Remove-Item env:\foo}... บางคนอาจบอกว่าเทอะทะแต่จะหลีกเลี่ยงผลข้างเคียง ...
Lucas

16

ฉันมีแรงบันดาลใจมากพอเกี่ยวกับปัญหานี้ที่ฉันดำเนินการต่อและเขียนสคริปต์สำหรับมัน: with-env.ps1

การใช้งาน:

with-env.ps1 FOO=foo BAR=bar your command here

# Supports dot-env files as well
with-env.ps1 .\.env OTHER_ENV=env command here

ในทางกลับกันหากคุณติดตั้งGowคุณสามารถใช้env.exeซึ่งอาจมีประสิทธิภาพมากกว่าสคริปต์ด่วนที่ฉันเขียนไว้ข้างต้นเล็กน้อย

การใช้งาน:

env.exe FOO=foo BAR=bar your command here

# To use it with dot-env files
env.exe $(cat .env | grep.exe -v '^#') SOME_OTHER_ENV=val your command

7

2 วิธีง่ายๆในการทำในบรรทัดเดียว:

$env:FOO='BAR'; .\myscript; $env:FOO=''
$env:FOO='BAR'; .\myscript; Remove-Item Env:\FOO

ข้อมูลสรุปจากคำตอบอื่น ๆ (ขอบคุณคนอื่น ๆ ) ซึ่งไม่มีหนึ่งบรรทัดด้วยเหตุผลบางประการ


4

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

function cmd_special()
{
  $orig_master = $env:app_master
  $env:app_master = 'http://host.example.com'
  mycmd $args
  $env:app_master = $orig_master
}

ดังนั้นเป็นปฏิบัติการที่ดำเนินการบางอย่างที่แตกต่างกันขึ้นอยู่กับค่าของตัวแปรสภาพแวดล้อมmycmd app_masterด้วยการกำหนดcmd_specialตอนนี้ฉันสามารถดำเนินการcmd_specialจากบรรทัดคำสั่ง (รวมถึงพารามิเตอร์อื่น ๆ ) ด้วยapp_masterชุดตัวแปรสภาพแวดล้อม ... และจะได้รับการรีเซ็ต (หรือแม้กระทั่งยกเลิกการตั้งค่า) หลังจากดำเนินการคำสั่ง

สันนิษฐานว่าคุณสามารถดำเนินการเฉพาะกิจสำหรับการร้องขอครั้งเดียวได้

& { $orig_master = $env:appmaster; $env:app_master = 'http://host.example.com'; mycmd $args; $env:app_master = $orig_master }

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

with-env app_master='http://host.example.com' mycmd

บางทีกูรู PowerShell สามารถแนะนำได้ว่าจะเขียน cmdlet ดังกล่าวได้อย่างไร


1

คุณสามารถทำได้โดยเรียกใช้สคริปต์เป็นงาน :

Start-Job -InitializationScript { $env:FOO = 'BAR' } -FilePath .\myscript.ps1 |
    Receive-Job -Wait -AutoRemoveJob

คุณยังสามารถส่งผ่านอาร์กิวเมนต์ไปยังสคริปต์โดยใช้ArgumentListพารามิเตอร์ของStart-Job:

$jobArgs = @{
    InitializationScript = { $env:FOO = 'BAR' } 
    FilePath             = '.\myscript.ps1'
    ArgumentList         = 'arg1', 'arg2' 
}
Start-Job @jobArgs | Receive-Job -Wait -AutoRemoveJob

ข้อดีและข้อเสีย

  • คุณไม่จำเป็นต้องรีเซ็ตตัวแปรสภาพแวดล้อมหลังจากที่สคริปต์เสร็จสิ้น (ซึ่งจะต้องมีtry/ finallyทำอย่างถูกต้องแม้ว่าจะมีข้อยกเว้นก็ตาม)
  • ตัวแปรสภาพแวดล้อมจะเป็นแบบโลคัลสำหรับสคริปต์ที่เรียกใช้ จะไม่ส่งผลกระทบต่องานอื่น ๆ ที่อาจเปิดตัวควบคู่กันไป
  • สคริปต์จะทำงานในสภาพแวดล้อมที่ค่อนข้างโดดเดี่ยว ซึ่งหมายความว่าสคริปต์ที่เรียกใช้ไม่สามารถตั้งค่าตัวแปรของสคริปต์หลักได้ซึ่งจะต้องใช้Write-Outputเพื่อสื่อสารกลับไปยังสคริปต์หลัก นี่อาจเป็นข้อดีหรือข้อเสียขึ้นอยู่กับกรณีการใช้งาน

0

เมื่อพิจารณาว่า CMD เป็น CLI ดั้งเดิมบนเคอร์เนลของ Windows (และยังคงเป็นอินเทอร์เฟซอัตโนมัติสำหรับเครื่องมือจำนวนมาก) คุณอาจเรียกใช้สคริปต์ PowerShell ของคุณpowershell.exeจากพรอมต์ CMD หรืออินเทอร์เฟซที่ยอมรับคำสั่งคอนโซล CMD

หากคุณกำลังใช้ไฟล์ -Fileพารามิเตอร์เพื่อส่งผ่านสคริปต์ของคุณไปยังpowershell.exeไม่มีรหัส PowerShell อื่นใดที่สามารถใช้เพื่อตั้งค่าตัวแปรสภาพแวดล้อมเพื่อให้สคริปต์เข้าถึงได้ดังนั้นคุณสามารถตั้งค่าตัวแปรสภาพแวดล้อมของคุณในสภาพแวดล้อม CMD ก่อนที่จะเรียกpowershell.exe:

> set foo=bar && powershell.exe -File .\script.ps1

ซิงเกิล&จะใช้งานได้เช่นกัน แต่จะอนุญาตให้คำสั่งดำเนินการต่อหากsetล้มเหลวด้วยเหตุผลบางประการ (เป็นไปได้หรือไม่ฉันไม่รู้)

นอกจากนี้อาจจะปลอดภัยกว่า"foo=bar"ในการรวมเครื่องหมายคำพูดเพื่อไม่ให้ไม่มีสิ่งต่อไปนี้ถูกส่งผ่านไปsetเป็นเนื้อหาตัวแปร


-5

คุณสามารถกำหนดขอบเขตตัวแปรเป็นฟังก์ชันและสคริปต์

$script:foo = "foo"
$foo
$function:functionVariable = "v"
$functionVariable

ตัวแปรใหม่ยังมีพารามิเตอร์ -scope หากคุณต้องการเป็นทางการและประกาศตัวแปรของคุณโดยใช้ตัวแปรใหม่


1
อืม ... อย่าคิดว่าคำตอบนี้ใช้ได้กับที่นี่ ตัวแปร PowerShell ไม่ใช่ตัวแปรสภาพแวดล้อม จากคำถามผู้ถามไม่ได้ใช้ตัวแปรสภาพแวดล้อมเป็นพื้นที่เริ่มต้น (เช่นเดียวกับที่ใช้ใน CMD [หากเป็นเช่นนั้นคำตอบนี้จะใช้ได้]) แต่จำเป็นต้องจัดการสภาพแวดล้อมก่อนที่จะเรียกใช้คำสั่งภายนอกจากนั้นจึงเรียกคืน สภาพแวดล้อมในภายหลัง (หรือบางสิ่งบางอย่างที่มีผลกระทบนั้น)
chwarr

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