我有以下代码:
$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输入范围并获得与第一个代码相同的结果?
你的问题是你的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
(其他优化也可以应用于您的脚本。)
至于何时将未引用的标记视为表达式与参数模式中的(可扩展)字符串(请参阅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
的值)。$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