簡體   English   中英

我如何有一個從 args 或 Powershell 管道中獲取輸入的數組參數?

[英]How do I have an array parameter that takes input from the args or the pipeline in Powershell?

我正在嘗試編寫一個接受數組參數的 Powershell 函數。 我希望使用數組作為參數或管道輸入調用它。 所以,調用看起來像這樣:

my-function -arg 1,2,3,4
my-function 1,2,3,4
1,2,3,4 | my-function

獲得前兩個很容易:

function my-function {
    param([string[]]$arg)
    $arg
}

然而,對於管道輸入,它更難。 通過使用 ValueFromPipeline,可以輕松地在進程塊中一次獲取一個參數,但這意味着 $args 變量是具有管道輸入的單個值,但如果使用 -args 則是一個數組。 我可以在 END 塊中使用 $input ,但這根​​本不會獲得 -args 輸入,並且在 END 塊中使用 $args 只能從管道中獲取最終項目。

我想我可以通過使用 begin/process/end 塊從管道中顯式收集參數值來做到這一點,如下所示:

function my-function {
    param([Parameter(ValueFromPipeline=$true)][string[]]$args)

    begin {
        $a = @()
    }
    process {
        $a += $args
    }

    end {
        # Process array here
        $a -join ':'
    }
}

但這似乎非常混亂。 這對我來說似乎也是一個相對常見的要求,所以我希望它很容易實現。 有沒有我錯過的更簡單的方法? 或者如果沒有,有沒有辦法將參數處理封裝到一個子函數中,這樣我就不必在我想要像這樣工作的每個函數中都包含所有這些?

我的具體要求是我正在編寫將 SQL 命令作為輸入的腳本。 因為 SQL 可能很冗長,所以我希望允許在命令中進行管道傳輸(可能由另一個命令生成,或從文件中的 get-contents 生成),但也允許使用參數形式,以便快速 SELECT 語句。 所以我從管道中獲取了一系列字符串,或者作為參數。 如果我得到一個數組,我只想將它與“`n”連接起來形成一個字符串——逐行處理是不合適的。

我想另一個問題是,是否有更好的腳本設計可以使多行輸入變得更清晰?


謝謝 - 訣竅是不要使用 ValueFromPipeline 那么......

我在讓事情按照我想要的方式工作時遇到這么多麻煩的原因是,在我的測試腳本中,我使用 $args 作為我的參數變量的名稱,忘記了它是一個自動變量。 所以事情非常奇怪......

PS> 1,2,3,4 | ./args

PS> get-content args.ps1
param([string[]]$args)

if ($null -eq $args) { $args = @($input) }
$args -join ':'

多 :-)

使用自動變量$input

如果只需要管道輸入,則:

function my-function {
    $arg = @($input)
    $arg
}

但我經常使用這種組合方法(一個接受輸入作為參數或通過管道的函數):

function my-function {
    param([string[]]$arg)

    # if $arg is $null assume data are piped
    if ($null -eq $arg) {
        $arg = @($input)
    }

    $arg
}


# test
my-function 1,2,3,4
1,2,3,4 | my-function

這是使用 Powershell 2.0+ 的另一個示例

這個例子是如果不需要參數:

function my-function {
  [cmdletbinding()]
  Param(
    [Parameter(ValueFromPipeline=$True)]
    [string[]]$Names
  )

  End {
    # Verify pipe by Counting input
    $list = @($input)
    $Names = if($list.Count) { $list } 
      elseif(!$Names) { @(<InsertDefaultValueHere>) } 
      else { @($Names) }

    $Names -join ':'
  }
}

在沒有'elseif'的情況下,有一種情況會出錯。 如果沒有為 Names 提供值,則 $Names 變量將不存在並且會出現問題。 請參閱此鏈接以獲取解釋。

如果需要,那么它不必那么復雜。

function my-function {
  [cmdletbinding()]
  Param(
    [Parameter(Mandatory=$true,ValueFromPipeline=$True)]
    [string[]]$Names
  )

  End {
    # Verify pipe by Counting input
    $list = @($input)
    if($list.Count) { $Names = $list } 

    $Names -join ':'
  }
}

它完全按預期工作,現在我在編寫管道函數時總是引用該鏈接。

來自管道的價值

應該使用管道 (ValueFromPipeline),因為 PowerShell 是專門為它設計的。

$args

首先,兩者之間沒有真正的區別:
my-function -<ParamName> 1,2,3,4
my-function 1,2,3,4 (假設參數$ParamName位於第一個位置)。

關鍵是參數名稱$args只是一個不幸的選擇,因為$args是一個自動變量,因此不應用於參數名稱。 幾乎任何其他名稱(不在自動變量列表中)都應該像Sean M.的示例一樣,但是您應該實現您的 cmdlet,假設它將從管道中間調用(請參閱:強烈鼓勵開發准則)。
(如果你想完全正確地做到這一點,你應該給一個單數的名字,復數的參數名應該只在參數的值總是一個多元素值的情況下使用。)

中間

您問題中假設的 cmdlet 不是一個很好的例子,因為它只關心輸入並且只有一個輸出,因此我創建了另一個例子:

Function Create-Object {
    Param([Parameter(ValueFromPipeline=$true)][String[]]$Name)
    Begin {
        $Batch = 0
        $Index = 0
    }
    Process {
        $Batch++
        $Name | ForEach {
            $Index++
            [PSCustomObject]@{'Name' = $_; 'Index' = $Index; 'Batch' = $Batch}
        }
    }
}

它基本上從名稱列表( $Names = "Adam", "Ben", "Carry" )中創建自定義對象。
當您通過參數提供“$Names”時會發生這種情況:

Create-Object $Names

Name  Index Batch
----  ----- -----
Adam      1     1
Ben       2     1
Carry     3     1

(它使用ForEach cmdlet 遍歷$Name參數中的所有名稱。)

當您通過管道提供$Names時會發生這種情況:

$Names | Create-Object

Name  Index Batch
----  ----- -----
Adam      1     1
Ben       2     2
Carry     3     3

請注意,輸出是類似的(如果不是batch列,則輸出實際上是相同的)但現在對象是在 3 個單獨的批次中創建的,這意味着每個項目都在process方法和ForEach循環中進行迭代每批只迭代一個,因為$Name參數包含一個數組,每個process迭代一個項目。

用例

想象$Names來自緩慢的來源(例如不同的威脅或遠程數據庫)。 如果您使用管道處理$Names您的 cmdlet 可以開始處理$Names (並將新對象傳遞到下一個 cmdlet),即使不是所有$Names都可用。 與通過參數提供$Names相比,在您的 cmdlet 處理它們並將新對象傳遞到管道之前,需要首先收集所有$Names

我認為您可以通過使用輸入處理方法BEGINPROCESSEND塊來實現這一點。 我剛遇到這個。 這是我的控制台輸出,只是在玩這個,您可以通過將函數的主體放在PROCESS塊中來看到它的行為方式與您期望的一樣

不工作

λ ~ function test-pipe {
>>     param (
>>         # Parameter help description
>>         [Parameter(ValueFromPipeline=$true)]
>>         [String[]]
>>         $Texts
>>     )
>>     $Texts | % {Write-Host $_}
>> }
λ ~ "this", "that", "another"
this
that
another
λ ~ $s = @("this", "that", "another")
λ ~ $s
this
that
another
λ ~ $s | test-pipe
another
λ ~ test-pipe -Texts $s
this
that
another

在職的

λ ~ function test-pipe {
>>     param (
>>         # Parameter help description
>>         [Parameter(ValueFromPipeline=$true)]
>>         [String[]]
>>         $Texts
>>     )
>>     BEGIN {}
>>     PROCESS {$Texts | % {Write-Host $_}}
>>     END {}
>>
>> }
λ ~ $s | test-pipe
this
that
another
λ ~

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM