将单个以空格分隔的字符串作为多个参数传递

时间:2017-02-14 13:43:26

标签: powershell parameters parameter-passing destructuring

我有一个Powershell功能,我试图允许用户通过输入单词"添加"来添加或删除列表中的项目。或"删除"后跟一个以空格分隔的项目列表。我在下面有一个示例(稍加编辑,因此您可以将代码放入PowerShell提示符中进行测试,然后#34;直播")。

$Script:ServerList = @("Server01","Server02","Server03")
Function EditServerList (){
$Script:ServerList = $Script:ServerList |Sort -Unique
Write-host -ForegroundColor Green $Script:ServerList
$Inputs = $args
If ($Inputs[0] -eq "start"){
    $Edits =  Read-Host "Enter `"add`" or `"remove`" followed by a space-delimited list of server names"
    #"# EditServerList $Edits
    #   EditServerList $Edits.split(' ')
    EditServerList ($Edits.split(' ') |Where {$_ -NotLike "add","remove"})
    EditServerList start
} Elseif ($Inputs[0] -eq "add"){
    $Script:ServerList += $Inputs |where {$_ -NotLike $Inputs[0]}
    EditServerList start
} Elseif ($Inputs[0] -eq "remove"){
    $Script:ServerList = $Script:ServerList |Where {$_ -NotLike ($Inputs |Where {$_ -Notlike $Inputs[0]})}
    EditServerList start
} Else {
    Write-Host -ForegroundColor Red "ERROR!"
    EditServerList start
}
}
EditServerList start

如您所见,该函数接受一个参数列表。第一个参数在If / Then语句中计算,然后其余参数被视为要在列表中添加或删除的项。

我尝试了一些不同的方法,你可以在第一次IF评估中看到这些方法。

我有两个问题。

  1. 当我输入类似#34的内容时,添加Server05 Server06" (没有引号)它可以工作,但它也会在单词中添加"添加"。
  2. 当我输入"删除Server02 Server03" (没有引号)它根本不编辑数组。
  3. 有人可以指出我出错的地方,或建议更好的解决方法吗?

2 个答案:

答案 0 :(得分:2)

预先解决标题的一般性问题:

  • 当您将数组传递给函数(而不是其他任何内容)时,$Args会收到包含整个数组的参数 ,因此您必须使用$Args[0]来访问它。

    • 是一种使用 splatting 将数组作为单独参数传递的方法,但它需要一个中间变量 - 请参阅底部。
  • 为避免对此类问题造成混淆,请正式声明您的参数。

尝试以下方法:

$Script:ServerList = @("Server01", "Server02", "Server03")

Function EditServerList () {
  # Split the arguments, which are all contained in $Args[0],
  # into the command (1st token) and the remaining
  # elements (as an array).
  $Cmd, $Servers = $Args[0]
  If ($Cmd -eq "start"){
    While ($true) {
      Write-host -ForegroundColor Green $Script:ServerList
      $Edits =  Read-Host "Enter `"add`" or `"remove`" followed by a space-delimited list of server names"
      #"# Pass the array of whitespace-separated tokens to the recursive
      # invocation to perform the requested edit operation.
      EditServerList (-split $Edits)
    }
  } ElseIf ($Cmd -eq "add") {
    # Append the $Servers array to the list, weeding out duplicates and
    # keeping the list sorted.
    $Script:ServerList = $Script:ServerList + $Servers | Sort-Object -Unique
  } ElseIf ($Cmd -eq "remove") {
    # Remove all specified $Servers from the list.
    # Note that servers that don't exist in the list are quietly ignored.
    $Script:ServerList = $Script:ServerList | Where-Object { $_ -notin $Servers }
  } Else {
    Write-Host -ForegroundColor Red "ERROR!"
  }
}

EditServerList start
  • 注意如何在"start"分支内部使用循环以避免耗尽堆栈空间,如果您保持递归,则可能会发生这种情况。

  • $Cmd, $Servers = $Args[0]将参数数组(包含在传递的唯一参数中 - 见下文)解构为第一个标记 - (命令字符串addremove )和其余参数的数组(服务器名称)。

    • 将参数预先分离为命令和服务器名称数组简化了剩余的代码。

    • 将RHS拆分为第一个元素$var1, $var2 = <array>技术 - 指定为标量$var1 - 和< em>剩余元素 - 被指定为数组$var2,通常称为解构解包;它记录在Get-Help about_Assignment Operators中,虽然没有给它这样的名字。

  • -split $Edits使用方便的一元形式的-split运算符将用户输入分解为以空格分隔的标记数组,并将该数组传递给递归调用。

    • 请注意EditServerList (-split $Edits)传递数组参数 - 这就是必须使用$Args[0]来访问它的原因

    • 使用PowerShell的-split运算符(而不是.Split(' '))具有忽略前导空格和尾随空格以及忽略条目之间的多个空格的附加优势。
      一般来说,运营商-split优于[string]类型的.Split()方法 - 请参阅我的this answer

  • 不是-notin中使用接受数组作为RHS的包含运算符Where-Object { $_ -notin $Servers }来过滤掉$Servers中包含的服务器列表中的值。

至于您尝试的内容:

  • EditServerList ($Edits.split(' ') |Where {$_ -NotLike "add","remove"})(a)错误地尝试从参数数组中删除命令名称,即使递归调用需要它,但是(b)实际失败这样做,因为-like的RHS不支持数组。 (顺便说一句:因为你正在寻找完全字符串,-eq本来是更好的选择。)

  • 由于您将参数作为数组传递为第一个且仅参数,$Inputs[0]实际上是指整个array (命令名+服务器名),而不仅仅是它的第一个元素(命令名)。

    • 即使比较了整个数组,您也可以使用($Inputs[0] -eq "add") - 因为-eq运算符会执行数组过滤它的LHS是一个数组,返回一个匹配元素的子数组。由于add在元素中是,因此返回了1个元素的子数组,在 Boolean 上下文中,它是“truthy”。

    • 但是,您尝试使用where {$_ -NotLike $Inputs[0]}清除命令名称失败,而add 已删除 - 您实际上必须与$Inputs[0][0](原文如此)。

  • Where {$_ -NotLike ($Inputs |Where {$_ -Notlike $Inputs[0]})}未过滤掉任何内容,原因如下:

    • ($Inputs |Where {$_ -Notlike $Inputs[0]})始终返回空数组,因为-Notlike的RHS是数组,如上所述,它不是工作
    • 因此,该命令相当于Where {$_ -NotLike @() },它为LHS上的任何标量返回$True

使用 splatting

将数组作为单独的参数传递

参数 splatting (参见Get-Help about_Splatting)也适用于数组:

> function foo { $Args.Count } # function that outputs the argument count.

> foo @(1, 2)  # pass array
1  # single parameter, containing array

> $arr = @(1, 2); foo @arr # splatting: array elements are passed as indiv. args.
2

请注意中间变量是如何需要的,以及如何使用@而不是$作为前缀来执行splatting。

答案 1 :(得分:1)

我使用参数来修改ServerList,这样你就可以使用一行来添加和删除:

Function EditServerList {
    param(
        [Parameter(Mandatory=$true)]
        [string]$ServerList,
        [array]$add,
        [array]$remove
    )

    Write-Host -ForegroundColor Green "ServerList Contains: $ServerList"

    $Servers = $ServerList.split(' ')

    if ($add) {
        $Servers += $add.split(' ')
    }

    if ($remove) {
        $Servers = $Servers | Where-Object { $remove.split(' ') -notcontains $_ }
    }

    return $Servers
}

然后你可以这样调用这个函数:

EditServerList -ServerList "Server01 Server02 Server03" -remove "Server02 Server03" -add "Server09 Server10"

将返回:

Server01
Server09
Server10