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

Posted

技术标签:

【中文标题】如何在命令行上传递一系列值 - 将表达式作为参数传递【英文标题】:how do I pass a range of values on the command line - passing an expression as an argument 【发布时间】:2017-05-06 09:00:31 【问题描述】:

我有以下代码:

$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 调用中被视为 字符串文字 - 它被识别为 表达式.

强制识别为表达式,请将参数括在(...)中,grouping operator:

.\eraseme.ps1 (29..30+40+50..52)

同样适用,如果您想使用(另一个)命令的输出作为命令参数;例如:

# Pass the lines read from file paths.txt as an array to Get-ChildItem
# (Parameter -Path is implied in both commands).
Get-ChildItem (Get-Content paths.txt)

两旁: • $(...),subexpression operator,只有在两种情况下才需要:(a) 将整个语句,特别是循环和条件语句,嵌入到另一个语句中,以及 (b)在 "..." 中嵌入表达式、命令或语句,这是一个可扩展(插值)字符串。只需 (...) 就足以在语句中嵌入单个 commandexpression(即使在 变量赋值的 RHS 上也不需要这样做 em>)。虽然不太可能,但不必要地使用 $(...) 可能会产生副作用 - 请参阅 this answer。 • 您可以使您的脚本更健壮,方法是使用更具体的类型声明参数,在这种情况下,尝试使用字符串调用它会立即失败:[Parameter(Mandatory=$False)] [int[]] $srv_range (其他优化也可以应用于您的脚本。)


可选的背景信息

至于当一个 unquoted 标记被视为 表达式嵌套命令(可扩展) 参数模式中的字符串(另见:about_Parsing):

(...)$(...)@(...)它们自己在令牌的开头创建一个新的解析上下文,其中可以使用表达式甚至嵌套命令

(...) 对于单个 表达式或命令就足够了。 $(...)子表达式运算符)可以包含多个表达式/命令; @()array 子表达式运算符)也可以,并且它还确保其输出始终被视为 array

值得注意的是,如果不包含在上述任一表达式中,则无法识别以下表达式:

[...](类型文字)和对其成员的访问,例如[Environment]::Version ..(范围表达式)如1..10

如果在 标记的开头(...)$(...)@(...) 后面跟着附加字符,则第一个附加字符被视为 的开始新的、单独的论点

相比之下,如果它们之前有一个不带引号的文字或一个仅变量引用$(...) 的工作方式类似于 @ 内部987654352@(可扩展的字符串),(...) 开始一个作为表达式的 新参数@(...) 被视为文字 @(...) 再次开始一个新参数,它是表达。

@ 后跟 变量的名称(例如,@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)。

访问变量值的属性或使用索引或调用方法或嵌入任意命令 em>,必须将表达式括在$(...)中,例如v$($PSVersionTable.PSVersion)

一般来说,"..."中用嵌入的变量引用/表达式括起来是最安全的,因为它可以避免以下边缘情况: * 开始处的$(...)解释为可扩展字符串的一部分,它被视为单独的参数(例如,Write-Output $('ab')c 导致 两个 参数:$('ab') 和文字 c 的结果)。 * . 在标记的开始紧跟一个简单的变量引用或子表达式也会导致单独的参数强>. (例如,.$HOME 会产生 两个 参数:文字 .$HOME 的值)

注意:虽然展开的结果是一个字符串,但不一定保持一个:最终类型由参数的类型决定扩展值绑定到的当前命令的名称。

转义/引用:

PowerShell 的元字符比cmd.exe 多得多,一个值得注意的缺陷是, 必须被转义才能被视为文字,因为, 是PowerShell 的数组构造运算符。

转义单个字符,请在其前面加上`(反引号)

为了避免单独转义元字符,请将值括在"..."(双引号)@987654391中@(单引号)

如果您希望字符串被插值(扩展),请使用双引号,即,如果您希望能够嵌入变量引用和子表达式。

内部是一个双引号字符串,`-转义以下字符。将它们视为文字:` " $

使用单引号将值视为文字

内部是一个单引号字符串,将' 转义为''

单引号或双引号通常是在值中转义空格的最简单方法。

最后,请注意 --%,即所谓的 停止解析符号 (PSv3+),完全改变了所有剩余参数的解释:设计用于旧的cmd.exe 命令行,它停止解释该行的其余部分,除了cmd.exe-style %...% 环境变量的扩展。见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

【讨论】:

以上是关于如何在命令行上传递一系列值 - 将表达式作为参数传递的主要内容,如果未能解决你的问题,请参考以下文章

如何将数组的值作为第二个参数传递给 awk 的 split 函数?

Linux驱动开发-安装驱动参数传递

将 IPython 变量作为参数传递给 bash 命令

将 0 作为命令行参数传递的问题

如何将 lambda 表达式作为参数传递给 c++ 模板

如何将 vue.js 值作为参数传递给刀片中的路由