Как назначить и ссылаться на переменные среды, содержащие квадратные скобки в Powershell - PullRequest
2 голосов
/ 25 марта 2019

Когда 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 Ответ

5 голосов
/ 25 марта 2019

Написано с 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".

...