Как назначить и ссылаться на переменные среды, содержащие квадратные скобки в Powershell
Когда PSDrive не указан, работает следующее:
${[foo]}="bar"
echo ${[foo]}
Но следующее не работает
$env:${[foo]}="bar"
At line:1 char:1
+ $env:${[foo]}="bar"
+ ~~~~~
Variable reference is not valid. ':' was not followed by a valid variable name character. Consider using ${} to delimit the name.
At line:1 char:6
+ $env:${[foo]}="bar"
+ ~~~~~~~~~~~~~~
Unexpected token '${[foo]}="bar"' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : InvalidVariableReferenceWithDrive
${env:[foo]}="bar"
Cannot find path 'env:[foo]' because it does not exist.
At line:1 char:1
+ ${env:[foo]}="bar"
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (env:[foo]:String) [], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound
Следующее работает, хотя мне любопытно, есть ли для него краткий синтаксис:
Set-Item -LiteralPath env:${[foo]} -Value "bar"
Get-Item -LiteralPath env:${[foo]} | % {$_.Value}
Однако следующее не работает:
Set-Item -LiteralPath env:${[foo]2} -Value "bar"
Set-Item : Cannot process argument because the value of argument "name" is null. Change the value of argument "name" to a non-null value.
At line:1 char:1
+ Set-Item -LiteralPath env:${[foo]2} -Value "bar"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:String) [Set-Item], PSArgumentNullException
+ FullyQualifiedErrorId : SetItemNullName,Microsoft.PowerShell.Commands.SetItemCommand
1 ответ
Написано в 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'
работает просто отлично.
Обходной путь:
Используйте двойной (!) - `
-экранирование подстановочных метасимволов:
# 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.
Этот двойной
`
-эскейпинг необходим, это добавленная причуда - смотрите эту проблему 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"
,