PowerShell: ustawienie zmiennej środowiskowej tylko dla jednego polecenia

107

W systemie Linux mogę:

$ FOO=BAR ./myscript

aby wywołać „myscript” z ustawioną zmienną środowiskową FOO.

Czy coś podobnego jest możliwe w PowerShell, tj. Bez konieczności wcześniejszego ustawiania zmiennej, wywoływania polecenia, a następnie ponownego kasowania zmiennej?

Żeby wyjaśnić mój przypadek użycia - nie chcę tego używać jako części skryptu. Mam raczej skrypt innej firmy, którego zachowanie mogę kontrolować za pomocą zmiennych środowiskowych, ale w tym przypadku nie za pomocą argumentów wiersza poleceń. Możliwość przełączania się między pisaniem

$ OPTION=1 ./myscript

i

$ ./myscript

byłoby po prostu bardzo przydatne.

cud2k
źródło
Myślę, że moje pytanie brzmi: dlaczego miałbyś to zrobić? Myślę, że jest lepsze rozwiązanie.
EBGreen
23
To zwykle nie jest pomocne pytanie, @EBGreen. Fakt, że taka możliwość jest dostępna w powłokach UNIX-a, sugeruje, że można ją wykorzystać. Na początek: kontrolowanie nazwy użytkownika i adresu e-mail, których git używa do zatwierdzania. Nie ma dla nich opcji wiersza poleceń - musisz ustawić je w ~ / .gitconfig, .git / config w każdym repozytorium lub envars. Spośród tych opcji envars są wyraźnie łatwiejsze do ustawienia w locie (i wygodnie zastępują wartości w plikach). Jeśli więc chcę zmienić imię i nazwisko autora dla jednego „polecenia git” w programie PowerShell, jak to zrobić?
Mark Reed
2
Całkowicie się zgadzam, że pytanie, dlaczego jest to potrzebne, jest bezcelowe. Jest to tak powszechne, jak barszcz podczas wykonywania poleceń w wierszu poleceń w systemie Linux, a ci z nas, którzy są teraz zmuszeni cierpieć z powodu PowerShell (koszmar syntaktyczny, jeśli kiedykolwiek istniał), muszą stale szukać odpowiedzi na oczywiste techniki. Przez większość czasu nie istnieją nawet w PowerShell, chyba że liczysz pisanie długich skryptów do robienia błahych rzeczy. Policz mnie głęboko sfrustrowanego tą powłoką ...
Kim
2
Ta funkcja jest przedmiotem dyskusji w programie PowerShell 6 .
Franklin Yu
1
Dzięki za link, @FranklinYu, ale w tym momencie byłaby to miejmy nadzieję, że w niezbyt odległej przyszłej wersji po v7.0 .
mklement0

Odpowiedzi:

55

Generalnie lepiej byłoby przekazywać informacje do skryptu za pomocą parametru niż zmiennej globalnej (środowiskowej). Ale jeśli tego potrzebujesz, możesz to zrobić w ten sposób:

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

Zmienną środowiskową $ env: FOO można później usunąć w następujący sposób:

Remove-Item Env:\FOO
Keith Hill
źródło
2
Tylko myśl: czy nie mógłbyś po prostu odrodzić nowego procesu PowerShell, przekazując mu blok skryptu za pomocą parametru -Command? W ten sposób nie musisz później czyścić środowiska, ponieważ będzie to zawarte w procesie potomnym. Chociaż rozmawiam z MVP PowerShell, więc to prawdopodobnie nie działa :-)
Joey
2
Keith, mamy w Pscx blokadę środowiska push i blokadę środowiska pop dla dokładnie tego scenariusza ;-)
x0n,
2
Johannes, to też by działało, ale w jakiś sposób wygląda na oszustwo. :-) PSCX Push / Pop-EnvironmentBlock (ten, który zmieniłem, aby działał w ten sposób) działałby, ale nie ma obsługi automatycznego czyszczenia, jaką ma blok skryptów.
Keith Hill
1
Czy nie musisz env:fooprzywracać starej wartości (być może nieustawionej) zamiast ją usuwać?
chwarr
1
jak wspomniał @Joey, jeśli chcesz czysto robić env vars, możesz: & {$pre = $env:foo; $env:foo = 'bar'; ./myscript; if ($pre) {$env:foo = $pre} else {Remove-Item env:\foo}... niektórzy mogą powiedzieć , że są nieporęczni , ale unikną skutków ubocznych ...
Lucas
14

Ten problem zmotywował mnie na tyle, że zacząłem pisać scenariusz: with-env.ps1

Stosowanie:

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

Z drugiej strony, jeśli zainstalujesz Gow , możesz użyć, env.exektóry może być nieco bardziej niezawodny niż szybki skrypt, który napisałem powyżej.

Stosowanie:

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
kizzx2
źródło
5

2 proste sposoby na zrobienie tego w jednej linii:

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

Po prostu podsumowałem informacje z innych odpowiedzi (dziękuję), które z jakiegoś powodu nie zawierają czystych linijek.

Alexander Fadeev
źródło
4

Aby uzyskać odpowiednik składni Uniksa, musisz nie tylko ustawić zmienną środowiskową, ale musisz zresetować ją do poprzedniej wartości po wykonaniu polecenia. Osiągnąłem to dla typowych poleceń, których używam, dodając funkcje podobne do poniższych do mojego profilu PowerShell.

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

Więc mycmdjest jakiś plik wykonywalny, który działa inaczej w zależności od wartości zmiennej środowiskowej app_master. Definiując cmd_special, mogę teraz wykonywać cmd_specialz wiersza poleceń (w tym inne parametry) z app_masterustawioną zmienną środowiskową ... i zostaje zresetowana (lub nawet wyłączona) po wykonaniu polecenia.

Przypuszczalnie mógłbyś również zrobić to ad hoc dla pojedynczego wywołania.

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

To naprawdę powinno być łatwiejsze niż to, ale najwyraźniej nie jest to przypadek użycia, który jest łatwo obsługiwany przez PowerShell. Może przyszła wersja (lub funkcja innej firmy) ułatwi ten przypadek użycia. Byłoby miło, gdyby PowerShell miał cmdlet, który robiłby to, np:

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

Być może guru PowerShell może zasugerować, jak można napisać takie polecenie cmdlet.

Jason R. Coombs
źródło
0

Biorąc pod uwagę, że CMD jest natywnym interfejsem wiersza polecenia w jądrze systemu Windows (i nadal jest interfejsem automatyzacji dla wielu narzędzi), możesz wykonywać skrypt PowerShell za powershell.exepomocą wiersza polecenia CMD lub interfejsu, który akceptuje instrukcje konsoli CMD.

Jeśli używasz -Fileparametru do przekazania skryptu powershell.exe, żaden inny kod PowerShell nie może być użyty do ustawienia zmiennej środowiskowej, do której ma dostęp skrypt, więc zamiast tego możesz ustawić zmienne środowiskowe w środowisku CMD przed wywołaniem powershell.exe:

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

Pojedynczy &również zadziała, ale pozwoli na kontynuowanie polecenia, jeśli z setjakiegoś powodu się nie powiedzie. (Czy to w ogóle możliwe? Nie mam pojęcia.)

Poza tym bezpieczniejsze może być zawijanie "foo=bar"cudzysłowów, tak aby nic po nim nie było przekazywane setjako zawartość zmiennej.

NReilingh
źródło
-5

Zakres zmiennych można przypisać do funkcji i skryptów.

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

Nowa-zmienna ma również parametr -scope, jeśli chcesz być formalny i zadeklarować zmienną za pomocą nowej-zmiennej.

Andy Schneider
źródło
1
Hmmm ... Nie myśl, że ta odpowiedź ma tutaj zastosowanie. Zmienne programu PowerShell nie są zmiennymi środowiskowymi. Opierając się na pytaniu, pytający nie używa zmiennych środowiskowych jako miejsca na zarysowania (jak w CMD [gdyby tak było, ta odpowiedź miałaby zastosowanie]), ale musi manipulować środowiskiem przed wywołaniem zewnętrznego polecenia, a następnie przywrócić środowisko później (lub coś w tym rodzaju).
chwarr