问题
Is it possible to create a custom operator in Powershell? And, how would I do that? I've searched Google but nothing is coming up.
I'm referring to specifically an infix operator:
$ExampleList -contains "element"
I've created cmdlets (with Powershell and C#), modules, etc., so I just need the broad strokes.
回答1:
You can create a prefix operator but not an in-fix or post-fix operator e.g.:
Set-Alias ?: Invoke-Ternary
function Invoke-Ternary {
param(
[Parameter(Mandatory, Position=0)]
[scriptblock]
$Condition,
[Parameter(Mandatory, Position=1)]
[scriptblock]
$TrueBlock,
[Parameter(Mandatory, Position=2)]
[scriptblock]
$FalseBlock,
[Parameter(ValueFromPipeline, ParameterSetName='InputObject')]
[psobject]
$InputObject
)
Process {
if ($pscmdlet.ParameterSetName -eq 'InputObject') {
Foreach-Object $Condition -input $InputObject | Foreach {
if ($_) {
Foreach-Object $TrueBlock -InputObject $InputObject
}
else {
Foreach-Object $FalseBlock -InputObject $InputObject
}
}
}
elseif (&$Condition) {
&$TrueBlock
}
else {
&$FalseBlock
}
}
Use like so:
$toolPath = ?: {[IntPtr]::Size -eq 4} {"$env:ProgramFiles(x86)\Tools"} {"$env:ProgramFiles\Tools"}}
回答2:
I know this is really late to the game.. but figured I would offer a nasty hack alternative.. The only benefit would be to make something that "looks" similar to the C# operator.
Set-Alias ?: Invoke-Ternary
function Invoke-Ternary {
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[scriptblock]$Condition,
[Parameter(Mandatory=$true, Position=0)]
[scriptblock]$Expression
)
process {
$TrueExpression = ($PSCmdlet.MyInvocation.BoundParameters['Expression'] -split '(?: +)?;(?: +)?')[0]
$FalseExpression = ($PSCmdlet.MyInvocation.BoundParameters['Expression'] -split '(?: +)?;(?: +)?')[1]
if (Invoke-Command -ScriptBlock $Condition) {
Invoke-Command -ScriptBlock ([scriptblock]::Create($TrueExpression -replace '\{|\}'))
} else {
Invoke-Command -ScriptBlock ([scriptblock]::Create($FalseExpression -replace '\{|\}'))
}
}
}
{[IntPtr]::Size -eq 4} |?: {{'I like Soup'} ; {'I''d rather have toast'}}
{[IntPtr]::Size -eq 8} |?: {{'I like Soup'} ; {'I''d rather have toast'}}
While this is really nothing more than string mangling of the first expression separated by a semicolon (in favor of a colon), it illustrates that there are multiple ways to accomplish this behavior. That all being said.. I would LOVE to have official operators created for the Ternary and the Null-Coalescing operators.. While I am at it.. an official Use-Object (using statement (not directive)) would be nice as well. Until then I will continue with my little hacks, but its nice to dream.
回答3:
After thinking on it, this is what I came up with:
Not sure if it's a better answer than Jon Tirjan's
function Op {
Param (
# this operator is the reverse of -contains
[switch] $isIn,
# this operator is just an example
[switch] $Plus
)
if ($isIn) {
if ($args.Length -ne 2) {
throw "Requires two arguments"
}
return ($args[1] -contains $args[0])
}
elseif ($Plus) {
if ($args.Length -ne 2) {
throw "Requires two arguments"
}
return ($args[0] + $args[1])
}
}
Example usage:
PS> $ExampleList = @("alpha", "bravo", "charlie")
PS> Op "alpha" -isIn $ExampleList
True
PS> Op "delta" -isIn $ExampleList
False
PS> Write-Host ("Answer: " + (Op 5 -plus 7))
Answer: 12
回答4:
You can create a cmdlet and pipe the list into it.
For evaluating a list, be sure to use the End function instead of Process.
Process will automatically iterate the collection, which you don't want here.
Your script would look something like this:
$ExampleList | Test-Contains "element"
来源:https://stackoverflow.com/questions/29757842/how-to-create-a-custom-powershell-operator