Как назначить и ссылаться на переменные среды, содержащие квадратные скобки в 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",

Другие вопросы по тегам