[英]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 是專門為它設計的。
首先,兩者之間沒有真正的區別:
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
。
我認為您可以通過使用輸入處理方法BEGIN
、 PROCESS
和END
塊來實現這一點。 我剛遇到這個。 這是我的控制台輸出,只是在玩這個,您可以通過將函數的主體放在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.