PowerShell: установка переменной среды только для одной команды

110

В Linux я могу:

$ FOO=BAR ./myscript

для вызова «myscript» с установленной переменной окружения FOO.

Возможно ли что-то подобное в PowerShell, т.е. без необходимости сначала установить переменную, вызвать команду, а затем снова сбросить переменную?

Чтобы быть более ясным в отношении моего варианта использования - я не хочу использовать это как часть сценария. Скорее у меня есть сторонний сценарий, поведение которого я могу контролировать с помощью переменных среды, но в данном случае не аргументов командной строки. Так что возможность переключаться между набором текста

$ OPTION=1 ./myscript

а также

$ ./myscript

просто было бы очень удобно.

miracle2k
источник
Думаю, мой вопрос будет в том, зачем вам это нужно? Я думаю, что есть решение получше.
EBGreen
24
@EBGreen, обычно это не самый полезный вопрос. Тот факт, что эта возможность присутствует в оболочках UNIX, говорит о том, что ей есть применение. Вне моей головы: контроль имени пользователя и адреса электронной почты, которые git использует для коммитов. Для них нет параметров командной строки - вы должны установить их либо в ~ / .gitconfig, .git / config в каждом репозитории, либо в envars. Из этих опций envars явно проще установить на лету (и удобно переопределять значения в файлах). Итак, если я хочу изменить имя автора для одного "git commit" в PowerShell, как это сделать?
Марк Рид
2
Совершенно согласен, что спрашивать, зачем это нужно, бессмысленно. Это так же распространено, как борщ при выполнении из командной строки в Linux, и тем из нас, кто сейчас вынужден страдать с помощью powershell (синтаксический кошмар, если он когда-либо существовал), постоянно приходится искать ответы на очевидные методы. В большинстве случаев они даже не существуют в PowerShell, если не считать написания длинных скриптов для выполнения тривиальных задач. Считайте, что я глубоко разочарован этой оболочкой ...
Ким
2
Эта функция обсуждается для PowerShell 6 .
Франклин Ю
1
Спасибо за ссылку, @FranklinYu, но на данный момент, мы надеемся, что это будет не очень отдаленная будущая версия после v7.0 .
mklement0 03

Ответы:

56

Как правило, лучше передавать информацию в сценарий через параметр, а не через глобальную переменную (среды). Но если это то, что вам нужно, вы можете сделать это следующим образом:

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

Переменная окружения $ env: FOO может быть удалена позже следующим образом:

Remove-Item Env:\FOO
Кейт Хилл
источник
2
Подумайте только: не могли бы вы просто создать новый процесс PowerShell, передав ему блок сценария с помощью параметра -Command? Таким образом, вам не нужно впоследствии очищать среду, поскольку она будет содержаться в дочернем процессе. Хотя я говорю с PowerShell MVP, так что это, вероятно, не сработает :-)
Джоуи
2
Кейт, у нас есть push-environmentblock и pop-environmentblock в Pscx именно для этого сценария ;-)
x0n
2
Йоханнес, это тоже сработает, но почему-то похоже на обман. :-) PSCX Push / Pop-EnvironmentBlock (тот, который я изменил, чтобы заставить работать таким образом) будет работать, но у него нет поддержки автоматической очистки, которую имеет блок сценария.
Кейт Хилл,
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}... некоторые могут сказать громоздко , но избежать побочных эффектов ...
Лукас
14

Я был достаточно мотивирован этой проблемой, поэтому написал для нее сценарий: 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
kizzx2
источник
6

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переменной среды ... и она сбрасывается (или даже сбрасывается) после выполнения команды.

Предположительно, вы также можете сделать это ad-hoc для одного вызова.

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

Это действительно должно быть проще, но, очевидно, это не тот вариант использования, который легко поддерживается PowerShell. Возможно, будущая версия (или сторонняя функция) облегчит этот вариант использования. Было бы неплохо, если бы в PowerShell был командлет, который бы это делал, например:

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

Возможно, гуру PowerShell подскажет, как написать такой командлет.

Джейсон Р. Кумбс
источник
0

Учитывая, что CMD является собственным интерфейсом командной строки в ядре 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качестве содержимого переменной.

NReilingh
источник
-5

Вы можете добавлять переменные в функции и сценарии.

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

New-Variable также имеет параметр -scope, если вы хотите быть формальным и объявить свою переменную с помощью new-variable.

Энди Шнайдер
источник
1
Хммм ... Не думаю, что этот ответ применим здесь. Переменные PowerShell не являются переменными среды. Основываясь на вопросе, автор вопроса не использует переменные среды в качестве рабочего пространства (как в CMD [если бы это было так, этот ответ был бы применим]), но ему необходимо манипулировать средой перед вызовом внешней команды, а затем восстановить окружающая среда после (или что-то в этом роде).
chwarr