Как передать диапазон значений в командной строке - передать выражение в качестве аргумента

У меня есть следующий код:

$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
Другие вопросы по тегам