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

问题描述 投票:4回答:1

我有以下代码:

$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输入范围并获得与第一个代码相同的结果?

powershell parsing command-line syntax parameter-passing
1个回答
11
投票

你的问题是你的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

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


Optional background information

至于何时将未引用的标记视为表达式与参数模式中的(可扩展)字符串(请参阅about_Parsing):

  • (...)$(...)@(...)本身或在令牌的开头创建一个新的解析上下文,其中可以使用表达式甚至嵌套命令: (...)足以用于单个表达式或命令。 $(...)(子表达式运算符)可以包含多个表达式/命令;所以@()(数组子表达式运算符),它还确保其输出始终被视为数组。 值得注意的是,如果没有包含在上述其中一个内容中,则无法识别以下表达式: [...](类型文字)和访问其成员,如[Environment]::Version ..(范围表达式),如1..10 如果在令牌的开头,(...)$(...)@(...)后跟其他字符,则第一个附加字符被视为新的单独参数的开头。 相比之下,如果它们之前是一个不带引号的文字或仅变量引用,$(...)就像"..."(一个可扩展的字符串)一样工作,(...)开始一个新的参数,这是一个表达式,@(...)再次作为文字@(...)开始一个表达式的新参数。
  • @后跟包含参数值的集合或散列表的变量(例如,@params)的名称启动parameter splatting
  • @{ ... }可用于传递哈希表文字(例如,@{ key = 'value' })。
  • { ... }创建了一个脚本块([scriptblock])。
  • 它们本身或在令牌的开头,可以按原样使用变量引用,包括成员访问(属性访问,方法调用,索引): 像$HOME$PSVersionTable.PSVersion$someArray[0]$someString.ToUpper()这样的表达被认可,并作为其固有类型返回。 如果没有成员访问权限,即使用简单的变量引用(如$HOME),则后续字符(可能)被视为同一参数的一部分,然后将其解释为可扩展字符串 - 请参见下文。 对于成员访问,任何其他字符的第一个被认为是新参数的开始(例如,$foo.Length-more产生两个参数:$foo.Length和字符串文字-more的值)。
  • 其他所有内容都被视为可扩展字符串,即类似于双引号字符串的内容,除了元字符[1]仍然需要转义并且某些标记被解释为多个参数。 可扩展意味着嵌入的简单变量引用(例如,$HOME\Desktop$env:APPDATA\Test)被内插(用其字符串化值替换)。 请注意,这可能导致表示与控制台中显示的给定值的默认输出格式不同(例如,有关更多信息,请参阅this answer)。 如有必要,在{...}中包含一个变量名,以便将其与后续字符消除歧义(例如,${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
© www.soinside.com 2019 - 2024. All rights reserved.