With custom Powershell functions all function output is returned. I'm attempting to remove that limitation/confusion by writing a wrapper function that will return just $true or $false.
However, I'm struggling with the dynamic function call . . . specifically passing the arguments.
Note that both the function name and the function arguments are passed to "ExecBoolean".
Code Sample:
# Simplifies calls to boolean functions in Powershells scripts
# Helps solve this problem -> http://www.vistax64.com/powershell/107328-how-does-return-work-powershell-functions.html
function Foo([string]$sTest, [int] $iTest)
{
Write-Host "sTest [" $sTest "]."
Write-Host "iTest [" $iTest "]."
if ($iTest -eq 1)
{
return $true
}
else
{
return $false
}
}
function ExecBoolean ([string] $sFunctionName, [array] $oArgs)
{
Write-Host "Array Length = " $oArgs.Length ", Items = [" $oArgs[0..($oArgs.Length - 1)] "]"
$oResult = & $sFunctionName $oArgs[0..($oArgs.Length - 1)]
# Powershell returns all function output in oResult, just get the last item in the array, if necessary.
if ($oResult.Length -gt 0)
{
return $oResult[$oResult.Length - 1]
}
else
{
return $oResult
}
}
$oResult = ExecBoolean "Foo" "String1", 1
Write-Host "Result = " $oResult
Current Output:
Array Length = 2 , Items = [ String1 1 ]
sTest [ String1 1 ].
iTest [ 0 ].
Result = False
Desired Output:
Array Length = 2 , Items = [ String1 1 ]
sTest [ String1 ].
iTest [ 1 ].
Result = True
Is this possible in Powershell v1.0?
Thanks.
I would use Invoke-Expression like this:
function ExecBoolean ([string] $sFunctionName)
{
Write-Host "Array Length = " $args.Length ", Items = [" $args[0..($args.Length - 1)] "]"
$oResult = Invoke-Expression "$sFunctionName $($args -join ' ')"
# Powershell returns all function output in oResult, just get the last item in the array, if necessary.
if ($oResult.Length -gt 0)
{
return $oResult[$oResult.Length - 1]
}
else
{
return $oResult
}
}
ExecBoolean Foo String1 1
Note that:
I use $args which is imho more comfortable, because you don't to pass the arguments as a array. However, you could use your $oArgs as well with no problem (the only line that matters is the one with Invoke-Expression).
This will work only for simple types (strings, ints, ...), but not for e.g. FileInfo, because the objects are converted to strings in $($args -join ' ').
There is one more option, but it is too verbose: ExecBoolean Foo #{sTest="String1"; iTest=1}. So you would pass parameters in a hashtable. You would need to alter Foo to accept hashtable of course. In this case you could pass any type in, because you could call Foo using & operator.
See below
I would not recommend this approach, because of the limitations (but it can happen that maybe others will come up with better solution).
If you just want to ensure that no output is returned from Foo, you might create a script that parses other scripts that call Foo and checks that every call to "Foo" must be prepended with $null =.
If you switch to PowerShell V2, there is better way called splatting. For more info look at How and Why to Use Splatting (passing [switch] parameters)
$oResult = & $sFunctionName $oArgs[0..($oArgs.Length - 1)]
Here you're passing the function only one argument —an array— so the second parameter in Foo defaults to 0.
If you just want to discard output from a statement/expression, there are ways to do so, like piping it to Out-Null or "casting" to void.
Personally, I would just use:
functionName -as [bool]
This will coerce the results of the function into a boolean.
It's much more readable later on.
Hope this helps
Related
I have the following snippet of PowerShell:
$property = #{
Getter = { 80 };
}
$value = $property.Getter.Invoke()
$value.GetType() # Shows Name = Collection'1 instead of int
I would expect $value.GetType() to return Int32 but instead it returns a collection.
I can't just take element [0] because sometimes the Getter function will return an array.
How can I fix this?
You can strictly declare the type of the return value by this way. For example, the return type will be Double :
cls
$property = #{
Getter = [Func[Double]]{ 80 }
}
$value = $property.Getter.Invoke()
$value.GetType().Name
# Double
Say I have the following powershell code:
function GetImageInfo()
{
[OutputType([ImageInfo])]
[ImageInfo] $imageInfo = [ImageInfo]::new()
$imageInfo.Owner = "Me"
$imageInfo.PrimaryTechnology = "jpeg"
$imageInfo.OperatingSystem = "Windows"
$imageInfo.OperatingSystemVersion = "10"
return $imageInfo
}
class ImageInfo
{
[string] $Owner
[string] $PrimaryTechnology
[string] $OperatingSystem
[string] $OperatingSystemVersion
[string[]] $OptionalQualifiers
}
I now want to call GetImageInfo and put the value of Owner in a variable.
I can do it like this:
$info = GetImageInfo
$owner = $imageInfo.Owner
But I was surprised that this does not work:
# Throws an error
$owner = GetImageInfo.Owner
For what I am doing now the shorter option would be nice.
Is there a way to get a field directly from a method call in PowerShell?
You need to wrap function call in the parenthesis. This allows you to let output from a command participate in an expression:
$owner = (GetImageInfo).Owner
I am trying to pass an array to functions and fill it, and then print the results outside of the functions.
But the first function doesn't recognize the array list object I am passing to it.
Main file:
. $funcFile
$myParam = "Hello World"
$myObj = getMyObject $myParam
$myObj.myArrayList.Count # This works (outputs 0)
myFunction2 ($myObj.myArrayList)
$myObj.myArrayList.Count # This also works (outputs 0)
fncFile:
function getMyObject([String] $myParam) {
$myObj = #{
"myArrayList" = (New-Object System.Collections.ArrayList)
}
return $myObj
}
function myFunction2 ([System.Collections.ArrayList] $myArr){
$myArr.Count # This doesn't work (outputs nothing)
if($myArr -eq $null) {
Write-Host "Array List Param is null" # This condition is FALSE - nothing is printed
}
}
What am I doing wrong?
How can I use the same ArrayList in function2 and other inner functions?
If you want to pass a variable and modify it in function and use the result there are 2 ways:
Pass by value:
$arr = New-Object System.Collections.ArrayList
function FillObject([System.Collections.ArrayList]$array, [String] $myParam) {
return $array.Add($myParam)
}
$arr = FillObject -array $arr -myParam "something"
$arr.Count
Pass by reference (what you asked about)
[System.Collections.Generic.List[String]]$lst = (New-Object System.Collections.Generic.List[String]])
function FillObject([System.Collections.Generic.List[String]][ref]$list,[String] $myParam) {
$list.Add($myParam)
}
FillObject -list ([ref]$lst) -myParam "something"
$lst.Count
You have to add [ref] both in the function definition and when you pass the parameter. If this will help you - Powershell and C# rely on .NET, so their syntax is similar. C# way to use the ref:
int number = 1;
void Method(ref int refArgument)
{
refArgument = refArgument + 44;
}
Method(ref number);
I have a function as following. It generates a string as per the parameters passed.
function createSentenceAccordingly {
Param([Parameter(mandatory = $false)] [String] $name,
[Parameter(mandatory = $false)] [String] $address,
[Parameter(mandatory = $false)] [String] $zipcode,
[Parameter(mandatory = $false)] [String] $city,
[Parameter(mandatory = $false)] [String] $state)
$stringRequired = "Hi,"
if($name){
$stringRequired += "$name, "
}
if($address){
$stringRequired += "You live at $address, "
}
if($zipcode){
$stringRequired += "in the zipcode:$zipcode, "
}
if($name){
$stringRequired += "in the city:$city, "
}
if($name){
$stringRequired += "in the state: $state."
}
return $stringRequired
}
So, basically the function returns something depending upon the parameters passed. I wanted to avoid the if loops as much as possible and access all the parameters at once.
Can I access all the parameters in an array or a hashmap ? As I should be using named parameters, $args cannot be used here. If I could access all the parameters at once (may be in an array like $args or a hashamp), my plan is to use that to create the returnstring dynamically.
In the future, the parameters for the function will increase a lot and I donot want to keep on writing if loops for each additional parameter.
Thanks in advance, :)
The $PSBoundParameters variable is a hashtable that contains only the parameters explicitly passed to the function.
A better way for what you want might be to use parameter sets so that you can name specific combinations of parameters (don't forget to make the appropriate parameters mandatory within those sets).
Then you can do something like:
switch ($PsCmdlet.ParameterSetName) {
'NameOnly' {
# Do Stuff
}
'NameAndZip' {
# Do Stuff
}
# etc.
}
I have a code in C# which uses lambda expressions for delegate passing to a method. How can I achieve this in PowerShell. For example the following is a C# code:
string input = "(,)(;)(:)(!)";
string pattern = #"\((?<val>[\,\!\;\:])\)";
var r = new Regex(pattern);
string result = r.Replace(input, m =>
{
if (m.Groups["val"].Value == ";") return "[1]";
else return "[0]";
});
Console.WriteLine(result);
And this is the PowerShell script without the lambda-expression in place:
$input = "(,)(;)(:)(!)";
$pattern = "\((?<val>[\,\!\;\:])\)";
$r = New-Object System.Text.RegularExpressions.Regex $pattern
$result = $r.Replace($input, "WHAT HERE?")
Write-Host $result
Note: my question is not about solving this regular-expression problem. I just want to know how to pass a lambda expression to a method that receives delegates in PowerShell.
In PowerShell 2.0 you can use a script block ({ some code here }) as delegate:
$MatchEvaluator =
{
param($m)
if ($m.Groups["val"].Value -eq ";")
{
#...
}
}
$result = $r.Replace($input, $MatchEvaluator)
Or directly in the method call:
$result = $r.Replace($input, { param ($m) bla })
Tip:
You can use [regex] to convert a string to a regular expression:
$r = [regex]"\((?<val>[\,\!\;\:])\)"
$r.Matches(...)
Sometimes you just want something like this:
{$args[0]*2}.invoke(21)
(which will declare an anonymous 'function' and call it immediately.)
You can use this overload
[regex]::replace(
string input,
string pattern,
System.Text.RegularExpressions.MatchEvaluator evaluator
)
The delegate is passes as a scriptblock (lambda expression) and the MatchEvaluator can be accessed via the $args variable
[regex]::replace('hello world','hello', { $args[0].Value.ToUpper() })