【问题标题】:Splatting without assigning hash or array to a variable在不将散列或数组分配给变量的情况下进行喷溅
【发布时间】:2016-10-31 03:00:27
【问题描述】:

我有一个返回哈希表的命令。比如这个:

function Get-TestArgs() { return @{a=1;b=2;c=3} }

我想用它的返回值作为我其他函数的参数:

function Test($a, $b, $c) {
    Write-Host 'A' $a
    Write-Host 'B' $b
    Write-Host 'C' $c
}

这可以通过 PowerShell 的 splatting 功能实现:

$testargs = @{a=1;b=2;c=3}
Test @testargs

问题是我不想将哈希分配给中间变量。

我的想法是这样的:

Test (some splat syntax)(Get-TestArgs)

显然,这是行不通的,因为它只是创建一个包含哈希的数组并传递它:

Test @(Get-TestArgs)

有没有办法做到这一点?

我关心的原因

我正在编写将手动执行的指令。这些指令将调用脚本,但在调用之间仍然需要进行一些手动干预。所以我想尽量减少命令以减少出现问题的风险,比如其他命令遗留的变量或拼写错误。

【问题讨论】:

    标签: powershell parameter-passing


    【解决方案1】:

    我认为在没有变量的情况下使用 splatting 是不可能的。但是很少有解决方法可以实现您正在寻找的东西。

    1. 编写一个包装函数,该函数将采用哈希表并调用“真实”函数。如果你想直接调用这个函数,没有Get-TestArgs,你可以使用TestInner

      function Get-TestArgs() { return @{a=1;b=2;c=3} }
      
      function TestInner ($a, $b, $c)
      {
          write-host "a=" $a
          write-host "b=" $b
          write-host "c=" $c
      }
      
      function Test ($hash) {
          return TestInner @hash
      }
      
      Test (Get-TestArgs)
      TestInner 1 2 3
      
    2. 设计你的函数,使每个函数都接受一个哈希表作为唯一的参数——它是 1 的简化版本。它看起来有点难看——你不能仅仅通过查看它的声明来确定 Test 所需的参数。

      function Test ($hash)
      {
          write-host "a=" $hash.a
          write-host "b=" $hash.b
          write-host "c=" $hash.c
      }
      
      Test (Get-TestArgs)
      
    3. 使用 2. 中的方法,但为函数参数创建一个新类型,以更加“类型安全”:

      add-type -TypeDefinition @"
          public class TestArg {
              public int a = 0;
              public int b = 0;
              public int c = 0;
          }
      "@
      
      function Get-TestArgs() { return new-object -type "TestArg" -Property @{a=1;b=2;c=3} }
      
      function Test ([TestArg] $o)
      {
          write-host "a=" $o.a
          write-host "b=" $o.b
          write-host "c=" $o.c
      }
      
      
      Test (Get-TestArgs) 
      
    4. 利用参数集和管道。这类似于编写包装器,但目标函数也是包装器 - 根据参数集,它将正常执行或使用 splatting 再次调用自身,但参数集不同。

      function Test {
      param(
          [Parameter(ParameterSetName="set1",Position=0)][int]$a, 
          [Parameter(ParameterSetName="set1",Position=1)][int]$b, 
          [Parameter(ParameterSetName="set1",Position=2)][int]$c, 
          [Parameter(ParameterSetName="pipeline",ValueFromPipeLine=$true,Position=0)][Hashtable]$obj
      )
      
          if ($obj -ne $null) { return Test @obj }
      
          write-host "a=" $a
          write-host "b=" $c
          write-host "c=" $c
      
      }
      
      Test (Get-TestArgs)
      Get-TestArgs | Test        
      Test 1 2 3
      

    根据您需要创建多少个函数以及您打算如何调用它们,您可以选择其中一种解决方案。就个人而言,最后一个对我来说似乎是最优雅的选择。我会使用管道:Get-TestArgs | Test 在 PowerShell 中看起来比Test (Get-TestArgs) 更自然。 例如,Azure PowerShell commands 中的许多都旨在以这种方式工作。

    【讨论】:

    • 我也喜欢选项 4 - 管道。
    【解决方案2】:

    我相当肯定这不能通过喷溅来完成,因为:

    [Splatting] says, “Take whatever characters come next and assume they’re a variable name. Assume that the variable contains a hashtable, and that the keys are parameter names. Expand those out, and feed the values from the hashtable to those parameters.” That may sound like a long-winded explanation, but that’s what’s happening.

    我真的认为你能做的最好的事情是:

    function Test() {
        param($a, $b, $c)
        Write-Host 'A' $a
        Write-Host 'B' $b
        Write-Host 'C' $c
    }
    
    function Get-TestArgs() { @(1,2,3) }
    
    $p = Get-TestArgs; Test @p
    

    【讨论】:

      【解决方案3】:

      我看到这篇文章可以追溯到 3 年前,但以防万一其他人(比如我)看到这篇文章,下面的解决方案可以工作,并且已经在 powershell 5 和 3 控制台(powershell.exe -版本 3)

      使用委托并调用它:

      &{ param($hashtable) &functionToRun @hashtable} (functionReturningHashTable)
      

      基于最初发布的代码:

      function Get-TestArgs() { return @{a=1;b=2;c=3} }
      
      function Test($a, $b, $c) {
          Write-Host 'A' $a
          Write-Host 'B' $b
          Write-Host 'C' $c
      }
      
      &{ param($hashtable) &Test @hashtable} (Get-TestArgs)
      

      返回: A 1 B 2 C 3

      您甚至可以将所有您想要的命令放在最后的括号中。

      假设您当前目录中有 3 个文件 a、b、c(如果不使用以下代码创建它们):

           'a','b','c' | % { New-Item -Path $_ -ItemType File }
      
           Mode                LastWriteTime         Length Name
           ----                -------------         ------ ----
           -a----       15/12/2019     14:34              0 a
           -a----       15/12/2019     14:34              0 b
           -a----       15/12/2019     14:34              0 c
      

      然后你可以执行以下命令:

      &{ param([hashtable]$hashtable) &Test @hashtable} (gci | group -AsHashTable -Property Name)
      

      返回:

           A a
           B b
           C c
      

      使用以下代码删除创建的文件:

           'a','b','c' | % { Remove-Item -Path $_ }
      

      问候,

      【讨论】:

        【解决方案4】:

        这可以很容易地完成,无需(用户定义的)变量,只需将您的哈希表通过管道传输到 ForEach-Object(别名 %)并喷出自动变量 $PSItem(别名 $_)。

        Get-TestArgs | ForEach-Object {Test @PSItem}
        Get-TestArgs | % {Test @_}
        

        两者都返回:

        A 1
        B 2
        C 3
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-01-16
          • 2013-10-24
          • 2015-04-14
          • 1970-01-01
          相关资源
          最近更新 更多