Написано с PowerShell Core 6.2.0
Причина в том, что PowerShell обрабатывает следующее:
${<drive>:<name>}
как если бы вы указали:
Get-Content -Path <drive>:<name> # or, with assignment, Set-Content -Path ...
Эта нотация - хотя часто используется с диском Env:
(например, $env:Path
) - малоизвестна как общая парадигма с именем нотация переменных пространства имен , что объясняется в этом ответе .
Проблема заключается в использовании -Path
вместо -LiteralPath
, поскольку -Path
интерпретирует свой аргумент как выражение подстановочного знака .
Следовательно, [foo]
в ${env:[foo]}
- вместо того, чтобы использоваться как есть - интерпретируется как подстановочное выражение, которое соответствует одиночному символу, который является либо f
, либо o
( [foo]
- это набор символов или диапазон ([...]
), который соответствует любому из (различных) символов внутри - см. about_Wildcards ).
При назначении на ${env:[foo]}
логика Set-Content -Path
требует, чтобы путь на основе подстановочного знака разрешал что-то существующее , даже если вы обычно не требуется для явного создания переменных среды; например, ${env:NoSuchVarExistsYet} = 'new'
работает просто отлично.
Обход
Использование double (!) - `
-экранирование метасимволов подстановочных знаков :
# Namespace variable notation only works with if you
# double(!)-backtick-escape the wildcard metacharacters:
# Assign to / implicitly create env. var '[foo]'
${env:``[foo``]} = 'bar'
# Get its value.
${env:``[foo``]}
Примечание:
Экранирование вообще не требуется , потому что нет веских оснований рассматривать пути, которые концептуально идентифицируют данный известный элемент как подстановочные выражения - см. эту проблему GitHub .
То, что double `
- требуется экранирование - это еще одна причуда - см. этот выпуск GitHub .
Другой обходной путь - тот, который не связан с побегом - должен использовать
Set-Content -LiteralPath env:[foo] bar
и Get-Content -LiteralPath env:[foo]
, но это и многословно, и медленно.
Что касается других вариантов синтаксиса, которые вы пробовали :
$env:${[foo]}="bar"
Поскольку ваша ссылка на переменную не {...}
-закрыта в целом (кроме начального $
), токен, следующий за :
, может содержать только символы, которые не требуют экранирования - и $
, {
и }
все нарушают это правило.
{...}
-Закрытие всего пути - ${env:[foo]}
- решает проблему синтаксис , но сталкивается с проблемой, описанной выше.
Set-Item -LiteralPath env:${[foo]} -Value "bar"
Это не работает вообще, потому что раскрытие строки применяется здесь заранее - это как если бы вы передали "env:${[foo]}"
: ссылка на (обычную) переменную с именем ${[foo]}
является расширенным (заменяется его значением) и фактически добавляется к буквальному env:
, до , передавая результат Set-Item
.
Если такая обычная переменная не существует, то Set-Item
видит только env:
(потому что для несуществующих переменных по умолчанию установлено значение $null
, которое становится пустой строкой в строковом контексте), что вызывает ошибку из-за отсутствия имени переменной.
В противоположность этому, следующее установило бы переменную окружения с именем unrelated
вместо:
# Create a regular variable literally named '[foo]'.
${[foo]} = 'unrelated'
# !! The following sets env:unrelated, i.e., env. var 'unrelated',
# !! due to the string expansion that is performed on the -LiteralPath
# !! argument up front.
Set-Item -LiteralPath env:${[foo]} bar
$env:unrelated # -> 'bar'
То же самое относится к Get-Item -LiteralPath env:${[foo]}
и
Set-Item -LiteralPath env:${[foo]2} -Value "bar"
.