【问题标题】:Pass a single space-delimited string as multiple arguments将单个空格分隔的字符串作为多个参数传递
【发布时间】:2017-02-14 13:43:26
【问题描述】:

我有一个 Powershell 函数,我试图允许用户通过键入单词“add”或“remove”后跟以空格分隔的项目列表来从列表中添加或删除项目。我在下面有一个示例(稍作编辑,因此您可以将代码放入 powershell 提示符以“实时”测试它)。

$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. 当我输入“add Server05 Server06”(不带引号)之类的内容时,它可以正常工作,但它也会包含“add”一词。
  2. 当我输入“删除 Server02 Server03”(不带引号)时,它根本不会编辑数组。

谁能指出我哪里出错了,或者提出一个更好的方法来解决这个问题?

【问题讨论】:

    标签: powershell parameters parameter-passing destructuring


    【解决方案1】:

    提前解决标题的一般性问题:

    • 当您将 array 传递给函数(仅此而已)时,$Args 会收到一个 single 参数包含整个数组,所以你必须使用$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" 分支中如何使用循环 以避免耗尽堆栈空间,如果你继续递归,可能会发生这种情况。 p>

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

      • 预先将参数分成命令和服务器名称数组可以简化剩余代码。

      • $var1, $var2 = <array> 技术将 RHS 拆分为其 第一个元素 - 作为 标量 分配给 $var1 - 和 剩余元素 - 作为一个 array 分配给$var2,通常称为 destructuringunpacking;它记录在 Get-Help about_Assignment Operators 中,尽管没有给出这样的名称。

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

      • 请注意,EditServerList (-split $Edits) 传递 single 参数,该参数是一个 数组 - 这就是为什么必须使用 $Args[0] 来访问它。

      • 使用 PowerShell 的 -split 运算符(与 .Split(' ') 相对)具有忽略前导和尾随空格以及忽略条目之间的多个空格的额外优势。
        一般来说,运算符-split[string] 类型的.Split() 方法更可取——参见我的this answer

    • 不知道如何在Where-Object { $_ -notin $Servers } 中使用包含运算符-notin,它接受一个数组作为RHS,以便从$Servers 中包含的服务器列表中过滤掉值。

      李>

    至于你尝试了什么:

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

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

      • 你侥幸逃脱了 ($Inputs[0] -eq "add") - 即使比较了整个 数组 - 因为 -eq 运算符执行 数组过滤 如果它的 LHS 是数组,返回匹配元素的子数组。由于add 个元素中,因此返回了一个包含1 个元素的子数组,它在布尔 上下文中是“真实的”。

      • 但是,您尝试使用 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 上的 any 标量返回 $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
    

    注意中间变量是如何需要的,以及它必须以@而不是$作为前缀来执行喷溅。

    【讨论】:

    • 嗨 mklement0,首先,关闭,感谢您提供非常详细的答案。我很欣赏你在回复中的想法。您能否帮助我进一步了解您发布的以下行发生了什么? $Cmd, $Servers = $Args[0] $Args[0] 左边有两个变量,$Cmd 对 $Args[0] 和 $Servers 对数组其余部分的影响?这意味着如果我要输入以下内容... $Cmd1, $Cmd2, $Servers = $Args[0] ... $Cmd1 将获得 $Args[0],$Cmd2 将获得 $Args[1] ,而 $Servers 会得到剩下的东西吗?
    • @JDDellGuy:我的荣幸;您对作业的工作方式是正确的;请查看我的更新,其中添加了说明和文档链接。
    【解决方案2】:

    我会使用参数来修改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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-25
      • 1970-01-01
      • 1970-01-01
      • 2011-04-11
      相关资源
      最近更新 更多