如何在命令行上传递一系列值 - 将表达式作为参数传递

use*_*279 4 syntax powershell command-line parsing parameter-passing

我有以下代码:

$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
    }
} 
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

我想从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
Run Code Online (Sandbox Code Playgroud)

如何从CLI输入范围并获得与第一个代码相同的结果?

mkl*_*nt0 11

您的问题是在您的调用中将参数29..30+40+50..52视为字符串文字.\eraseme.ps1 29..30+40+50..52 - 它不会被识别为表达式.

强制识别为表达式,只需将参数括在(...):

.\eraseme.ps1 (29..30+40+50..52)
Run Code Online (Sandbox Code Playgroud)

请注意,通过使用更具体的类型声明参数,可以使脚本更加健壮,在这种情况下,尝试使用字符串调用它会立即失败:

 [Parameter(Mandatory=$False)] [int[]] $srv_range
Run Code Online (Sandbox Code Playgroud)

(其他优化也可以应用于您的脚本.)


可选的背景信息

至于何时将不带引号的参数视为表达式(可扩展)字符串(在参数模式下):

  • (...),$(...)@(...)创建一个新的解析上下文,其中可以使用表达式甚至嵌套命令:

    • (...)足以表达单个表达式.$(...)(子表达式运算符)可以包含多个表达式/命令; 也可以@()(数组子表达式运算符),但它还确保其输出始终被视为数组.

    • 值得注意的是,如果没有以上某种情况,则无法识别以下表达式:

      • [...] (类型引用)如 [Environment]::Version
      • .. (范围表达式)如 1..10
  • A (...)后跟包含参数值的集合或散列表的变量(例如$(...))的名称启动参数splatting.

  • @(...)可用于传递哈希表文字(例如$(...)).

  • 它们本身可以按原样使用变量引用,包括索引/属性访问,甚至方法调用:

    • 就其本身而言,令牌等"...",(...)以及@(...) 认可,并返回其固有的类型.你甚至可以用这种方式调用方法(例如@).

    • 如果直接在变量引用或方法调用之前添加或附加字符,则规则会更改 - 请参阅下文.

  • 其他一切被视为可扩展字符串,即,类似的双引号字符串的内容,除了元字符[1]仍然需要转义某些令牌被解释为多个参数.

    • 可扩展意味着嵌入的简单变量引用(例如,(...)@)被内插(用其字符串化值替换).
      请注意,这可能会导致表示与控制台中显示的给定值的默认输出格式不同(再次,请参阅此答案以获取更多信息).

      • @params如有必要,将变量名称括在其中以便与后续字符消除歧义(例如,@{ ... }).
    • 要访问变量值的属性或使用索引或调用方法或嵌入任意命令,必须将表达式括在@{ key = 'value' },例如,{ ... }

    • 通常,最安全的是使用嵌入的变量引用/表达式来包含标记[scriptblock],因为它避免了以下边缘情况:

      • $HOME在未加引号的标记的开头不会被解释为可扩展字符串的一部分,它被视为一个单独的参数(例如,$PSVersionTable.PSVersion产生两个参数:结果$someArray[0]和文字$someString.ToUpper()).
      • $HOME在令牌的开头紧接着是一个简单的变量引用或子表达式,也会导致单独的参数.
        (例如,$foo.Length-more结果有两个参数:文字$foo.Length和值-more)
    • 注意:即使扩展的结果是字符串,它也不一定一个:最终类型由扩展值绑定的手头命令参数类型决定.

    • 逃避/引用:

      • PowerShell有许多元字符$HOME\Desktop,并且一个值得注意的缺陷是$env:APPDATA\Test必须将其转义为文字处理,因为{...}PowerShell的数组构造运算符.

      • 转义单个字符,请在其${HOME}前面加上(反引号).

      • 避免单独转义元字符的需要,请将值括在$(...)(双引号)v$($PSVersionTable.PSVersion)(单引号)中:

        • 如果希望对字符串进行插值(展开),即希望能够嵌入变量引用和子表达式,请使用双引号.

          • 双引号字符串中,"..."- 以下字符.将它们视为文字: $(...)
        • 使用单引号将值视为文字.

          • 单引号字符串中,转义为Write-Output $('ab')c as$('ab')
      • 单引号或双引号通常是逃避值中空格的最简单方法.

  • 最后,请注意,c所谓的停止解析符号(PSv3 +)完全改变了对所有剩余参数的解释:设计用于遗留.命令行,它会停止解释除了.$HOME样式.环境变量的扩展之外的其余部分.看到$HOME


[1] 参数模式元字符(具有特殊句法意义的字符)是:
cmd.exe.
其中,,仅在令牌的开头是特殊的.


例子

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
Run Code Online (Sandbox Code Playgroud)