Как передать диапазон значений в командной строке - передать выражение в качестве аргумента
У меня есть следующий код:
$srv_range = 29..30+40+50..52
$srv_range.GetType()
$NewVMTemplate = New-Object psobject
$NewVMTemplate | Add-Member -MemberType NoteProperty -Name Name -Value $null
$srv_range | % {
$pod= $_
$servers = @()
1..2 | % {
$server = $NewVMTemplate | Select-Object *
$server.Name = "pod" + "{0:D2}" -f $pod + "-srv" + $_
$servers += $server
}
ForEach ( $server in $servers) {
write-host $server.Name
}
}
выход:
PowerCLI C:\ .\eraseme.ps1
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
pod29-srv1
pod29-srv2
pod30-srv1
pod30-srv2
pod40-srv1
pod40-srv2
pod50-srv1
pod50-srv2
pod51-srv1
pod51-srv2
pod52-srv1
pod52-srv2
Я хочу ввести диапазон из CLI, но я получаю следующий вывод с этим кодом
param(
[Parameter(Mandatory=$False)] $srv_range
)
#$srv_range = 29..30+40+50..52
$srv_range.GetType()
$NewVMTemplate = New-Object psobject
$NewVMTemplate | Add-Member -MemberType NoteProperty -Name Name -Value $null
$srv_range | % {
$pod= $_
$servers = @()
1..2 | % {
$server = $NewVMTemplate | Select-Object *
$server.Name = "pod" + "{0:D2}" -f $pod + "-srv" + $_
$servers += $server
}
ForEach ( $server in $servers) {
write-host $server.Name
}
}
PowerCLI C:\ .\eraseme.ps1 29..30+40+50..52
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
pod29..30+40+50..52-srv1
pod29..30+40+50..52-srv2
Как я могу ввести диапазон из CLI и получить тот же результат, что и первый код?
1 ответ
Ваша проблема в том, что аргумент 29..30+40+50..52
трактуется как строковый литерал в вашем .\eraseme.ps1 29..30+40+50..52
вызов - это не распознается как выражение.
Чтобы заставить распознавание как выражение, просто заключите аргумент в (...)
:
.\eraseme.ps1 (29..30+40+50..52)
Обратите внимание, что вы можете сделать ваш скрипт более устойчивым, объявив ваш параметр более конкретным типом, и в этом случае попытка вызвать его с помощью строки сразу же потерпит неудачу:
[Parameter(Mandatory=$False)] [int[]] $srv_range
(Другие оптимизации также могут быть применены к вашему сценарию.)
Дополнительная справочная информация
Что касается случая, когда аргумент без кавычек обрабатывается как выражение против (расширяемой) строки (в режиме аргумента):
(...)
,$(...)
, а также@(...)
создайте новый контекст синтаксического анализа, в котором могут использоваться выражения или даже вложенные команды:(...)
достаточно для одного выражения.$(...)
(оператор подвыражения) может содержать несколько выражений / команд; так может@()
(оператор подвыражения массива), но он дополнительно гарантирует, что его вывод всегда обрабатывается как массив.Примечательно, что следующие выражения не распознаются без одного из вышеперечисленных:
[...]
(тип ссылки), такие как[Environment]::Version
..
(выражения диапазона), такие как1..10
@
сопровождаемый именем переменной (например,@params
), содержащий коллекцию или хэш-таблицу значений параметров, инициирует разделение параметров.@{ ... }
может использоваться для передачи хеш-таблицы (например,@{ key = 'value' }
).Сами по себе ссылки на переменные, включая доступ к индексу / свойству и даже вызовы методов, могут использоваться как есть:
Сами по себе токены, такие как
$HOME
,$PSVersionTable.PSVersion
, а также$someArray[0]
распознаются и возвращаются как их неотъемлемый тип. Вы даже можете вызвать метод таким образом (например,$PSVersionTable.ContainsKey('PSVersion')
).Правила изменяются, если вы непосредственно добавляете или добавляете символы к ссылке на переменную или к вызову метода - см. Ниже.
Все остальное обрабатывается как расширяемая строка, т. Е. Аналогично содержимому строки в двойных кавычках, за исключением того, что метасимволы [1] по- прежнему нуждаются в экранировании, а некоторые токены интерпретируются как множественные аргументы.
Расширяемый означает, что встроенные ссылки на простые переменные (например,
$HOME\Desktop
или же$env:APPDATA\Test
) интерполируются (заменяются их строковыми значениями).
Обратите внимание, что это может привести к представлению, которое отличается от формата вывода данного значения по умолчанию, как показано, например, в консоли (снова, см. Этот ответ для получения дополнительной информации).- Вложите имя переменной в
{...}
при необходимости отстранить его от последующих символов (например,${HOME}
).
- Вложите имя переменной в
Чтобы получить доступ к свойству значения переменной или использовать индекс, вызвать метод или встроить произвольные команды, вы должны заключить выражение в
$(...)
например,v$($PSVersionTable.PSVersion)
Как правило, безопаснее всего включать токены со встроенными ссылками / выражениями переменных в
"..."
, потому что это позволяет избежать следующих крайних случаев:$(...)
в начале токен без кавычек не интерпретируется как часть расширяемой строки, он обрабатывается как отдельный аргумент (например,Write-Output $('ab')c
приводит к двум аргументам: результат$('ab')
и буквальныйc
)..
в начале токена, за которым сразу же следует простая ссылка на переменную или подвыражение, также приводятся отдельные аргументы.
(Например,.$HOME
приводит к двум аргументам: буквальный.
и значение$HOME
)
Примечание. Несмотря на то, что результатом раскрытия является строка, она не обязательно остается одной: конечный тип определяется типом параметра команды, с которой связано расширенное значение.
Побег / цитирование:
PowerShell имеет гораздо больше метасимволов, чем
cmd.exe
и заметная ошибка в том, что,
нужно избегать, чтобы относиться к буквальному, потому что,
является оператором построения массивов PowerShell.Чтобы экранировать один символ, добавьте к нему префикс
`
(обратный удар).Чтобы избежать необходимости экранирования метасимволов по отдельности, заключите значение в
"..."
(двойные кавычки) или'...'
(одинарные кавычки):Используйте двойные кавычки, если вы хотите, чтобы строка была интерполирована (развернута), т. Е. Если вы хотите иметь возможность вставлять переменные ссылки и подвыражения.
- Внутри строки в двойных кавычках,
`
-Сохранить следующие символы. относиться к ним как к литералам:` " $
- Внутри строки в двойных кавычках,
Используйте одинарные кавычки для обработки значения как литерала.
- Внутри строки в одинарных кавычках, экранируйте
'
как''
- Внутри строки в одинарных кавычках, экранируйте
Одинарные или двойные кавычки, как правило, являются самым простым способом избежать пробелов в значении.
Наконец, обратите внимание, что
--%
, так называемый символ остановки парсинга (PSv3+), полностью меняет интерпретацию всех оставшихся аргументов: предназначен для использования с устаревшимcmd.exe
командные строки, он останавливает интерпретацию остальной части строки, за исключением расширенияcmd.exe
-стиль%...%
переменные среды УвидетьGet-Help about_Parsing
[1] Метасимволы режима аргумента (символы со специальным синтаксическим значением):
<space> ' " ` , ; ( ) { } | & < > @ #
,
Из этих, < > @ #
являются особенными только в начале токена.
Примеры
Write-Output 1..10 # STRING: -> '1..10'
Write-Output (1..10) # EXPRESSION: -> @(1, 2, ...)
# Write-Output $(1..10) would work too, but is only necessary if
# the enclosed expression comprises *multiple* statements.
Write-Output [Environment]::Version # STRING: -> '[Environment]::Ticks'
Write-Output ([Environment]::Version) # EXPRESSION: -> a [System.Version] instance.
Write-Output a,b # !! ARRAY @(1, 2), because "," is not escaped.
Write-Output a`,b #`# STRING 'ab'
Write-Output "a,b" # ditto
Write-Output 'a,b' # ditto
Write-Output $HOME\Desktop # EXPANDED string (e.g.) 'C:\Users\jdoe\Desktop'
Write-Output "$HOME\Desktop" # ditto
Write-Output '$HOME\Desktop' # LITERAL string '$HOME\Desktop'
Write-Output dir=$HOME # EXPANDED string (e.g.) 'dir=C:\Users\jdoe\Desktop'
Write-Output $PSVersionTable.PSVersion # a [System.Version] instance
Write-Output "$($PSVersionTable.PSVersion)/more" # a [string]; e.g., '5.1.14393.576/more'
Write-Output "v$($PSVersionTable.PSVersion)" # ditto; e.g., 'v5.1.14393.576'
# !!! These DO NOT WORK as intended.
Write-Output $($PSVersionTable.PSVersion)/more # $(...) at the *start*
Write-Output $PSVersionTable.PSVersion/more # $(...) missing
Write-Output "$PSVersionTable.PSVersion/more" # $(...) missing
Write-Output .$HOME # Specifically, .$ at the beginning is the problem; escaping . works