Форматирование строк PowerShell: почему символ двоеточия, заставляющий мое значение переменной быть пустым?
Я смотрел, что происходит со следующими WinRM и PowerShell 3, и я просматривал список изменений и видел то, чего я никогда раньше не видел.
Пример:
$server = "msp42"
$status = "online"
"$server: $status"
Результирующий результат:
онлайн
Хорошо, я никогда не сталкивался с этим раньше и не знаю, почему толстая кишка вызвала проблему.
Решение, предлагаемое в документе, заключалось в том, чтобы поместить пробел (что глупо, потому что тогда вы меняете выход):
"$server : $status"
Другое предложение заключалось в том, чтобы использовать этот формат (новый для меня!):
"${server}: $status"
Последнее предложение состояло в том, чтобы сделать выражение, которое я знаю и использую все время:
"$($server): $status"
Итак, мои вопросы к вам, гуру PowerShell, есть:
Ответы
Ответ 1
Двойник является допустимым символом для имен переменных, например. в $Env:PATH
и т.д.
Вы также можете использовать следующую опцию
$server`: $status
или, в некоторых случаях, строка формата более читаема:
'{0}: {1}' -f $server, $status
Вернемся к двоеточию. Существует специальный случай для имен переменных, которые соответствуют элементу на PSDrive:
$Env:Foo # equivalent to the contents of Env:\Foo
$Function:C: # equivalent to the contents of Function:\C:
${C:\autoexec.bat} # ... you get the picture
Синтаксис ${}
существует, чтобы иметь возможность указывать имена переменных, которые в противном случае используют символы, зарезервированные для других частей синтаксиса. Вы могли видеть, что это похоже (но более мощное) на С# @
перед идентификаторами. См. Выше, где a \
используется в имени переменной, так как $Drive:Item
работает только для текущего контейнера на диске (или для корневых для неиерархических, таких как Env
, Alias
или Function
).
Другой пример, в котором имя переменной обычно является недействительным синтаксисом:
PS> $+
The term '$+' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the
spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ $+
+ ~~
+ CategoryInfo : ObjectNotFound: ($+:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
PS> ${+} = 5
PS> ${+}
5
PS> Get-Variable +
Name Value
---- -----
+ 5
Ответ 2
Вот что-то, что сработало у меня на днях в Windows PowerShell script. Скажем, что вам нужно перечислить $user и $machine и хотите напечатать строку со значением обоих из них, разделенных двоеточием (':').
Вы можете, очевидно, сделать это, используя конкатенацию строк:
$user = 'tomasr'
$machine = 'arcano'
write ($user + ':' + $machine)
Это работает, но не поддерживает ли PowerShell эту прекрасную функцию интерполяции строк? Почему, да, это так! Так почему бы нам просто не переписать его так:
write "$user:$machine"
Но подождите! Если вы запустите эту последнюю строку, все, что вы получаете, это значение переменной $machine..., где сделал $user go?
Ответ заключается в том, что двоеточие имеет особое значение в именах переменных PowerShell: оно используется для связывания переменной с определенной областью или пространством имен. Существует несколько областей, определенных PowerShell, таких как script и global; поэтому, например, глобальную переменную можно назвать $global: var.
Но разве мы не видели этот синтаксис в другом месте? Конечно, как насчет $env: PATH? То же самое; кроме здесь env не является областью, а PSDrive. Таким образом, часть перед ":" может быть либо фактической областью, определенной PS, либо PSDrive. Черт, вы даже можете попробовать что-то вроде "$ {c: autoexec.bat}" и наблюдать за магией!
Таким образом, проблема с нашей интерполяцией строк заключается в том, что когда PowerShell видит "$user:$machine"
, он ожидает найти область /psdrive с именем "пользователь" и ожидает, что имя переменной будет следовать за ":", но "$" не valid, а "user" не является областью действия или PSDrive, что немного путает PowerShell. Но так как он слишком много джентльмен, он просто исправляет это как можно лучше и игнорирует все до этого момента и просто сбрасывает $машину, не жалуясь. Приятно, но он действительно может вас отпустить, если вы этого не ожидаете!
Можно ли это избежать? Да, мы можем, просто разделив имена переменных именно для PowerShell, используя "{}", например:
write "${user}:$machine"
Раздел 5.8 на стр. 141 Брюса Пейетта, отличный от Windows PowerShell в действии, охватывает тему переменных в PowerShell и дал мне ключ к решению этой проблемы.