Passing string in and back out of function - function

I am really new to Powershell but I have programmed in Java and other languages.
I am trying to pass a string to a function and then have that function return the string. Below is the simple code I am trying to run:
#funcpass.ps1
function test {
Param([string]$input)
$out = $input
return $out
}
$a = test hello world
Write-Host $a
I expect this to pass the string hello world then return the string into the variable $a to be printed. Instead my console returns this:
PS P:\test> .\funcpass.ps1
PS P:\test>
Is there some kind of scope error that I am encountering? Any help would be greatly appreciated. I am not sure if the version number helps, but here it is:
PS P:\test> $PSVersionTable.PSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 1198

The $input parameter is reserved, so you have to change its name. Moreover, if you want to pass only one string to the function you have to enclose it with quotes:
function Run-Test {
Param([string]$inputValue)
$out = $inputValue
return $out
}
$a = Run-Test "hello world"
Write-Host $a
FYI the return keyword is optional but it makes your intentions more clear as other language use return to indicate that something is being returned from the function. Everything sent on the pipeline inside the function (like Write-Output) will be returned.

Related

Check if parameter is missed

I have this code in my $profile in PS version 5:
function af_ {
Get-ChildItem function: | findstr.exe $args
if (! $args) {
return "nothing"
}
}
Calling e.g.
af_ tgit
Return:
Function tgit 0.7.3 posh-git
Calling
af_
Output:
FINDSTR: Syntaxfehler
nothing
Two questions:
How can I check if $args is not empty without "Syntaxfehler" (this is german..)?
Can I improve my idea to get the defnition of a custom function, as would be
declare -f $function
in Bash. It shows the definition which seems not possible in PS. I have to process the function: device and then look for the "Definition" output.
You get the syntax error because you call findstr.exe with the empty argument before checking it. Reversing the order should do it:
function af_ {
if (! $args) {
return "nothing"
}
Get-ChildItem function: | findstr.exe $args
}
As for your second question you can use Get-Command to get the command and it's definition:
(Get-Command af_).Definition

Get PowerShell function to write a table to the screen during a run but not append as a return

Here is a short example (my actual code requires me to output many more tables during the function run, and get a single returned output from it):
Function Select-RowFromCSV ($CSV)
{
$CSV
return $CSV[(read-host "select row # from CSV")]
}
Instead of outputting $CSV within the function it gets appended to the return and is getting into the variable that the function inserted to.
PS C:\Windows\system32> $ROW = Select-RowFromCSV -CSV (Import-Csv "C:\scripts\csv.csv")
select row # from CSV: 0
PS C:\Windows\system32> $ROW
Name Phone
Dan 111111
Dave 5555555
Oliver 666666
Dan 111111
PS C:\Windows\system32>
I tried multiple ways to try and print it to the screen, however unlike write-host that do work as expected for strings, none of the other one i tried works for non strings objects (FT, write-output, echo).
If you want to output something to the console without affecting the output of the Function, Write-Host is probably the simplest solution (but is considered harmful). Your code needs to be as follows:
Function Select-RowFromCSV ($CSV)
{
Write-Host ($CSV | Format-Table | Out-String)
Return $CSV[(read-host "select row # from CSV")]
}
$ROW = Select-RowFromCSV -CSV (Import-Csv "raid.csv")
As you observed, because your object isn't a string you need to Format it as you'd like and then convert it to a String (| Format-Table | Out-String).
However, you might want to consider using Write-Verbose which will write output to the Verbose stream instead, only when the -Verbose switch is used. To use this you need to add [cmdletbinding()] and a Param() block to your function, like this:
Function Select-RowFromCSV
{
[cmdletbinding()]
Param($CSV)
Write-Verbose ($CSV | Format-Table | Out-String)
Return $CSV[(read-host "select row # from CSV")-1]
}
Then execute your function with the -Verbose switch to see the extra output:
$ROW = Select-RowFromCSV -CSV (Import-Csv "raid.csv") -Verbose
Further Explanation:
In your original function you were outputting all of $CSV because it appeared on a line by itself, then also returning a row of it. The Return keyword is a little misleading in PowerShell, it doesn't define what is only returned, it just triggers the Function to immediately end and anything that has been output will go in to the output stream.
You should also note that the row number your user enters needs to start from 0, because Array indexes start from 0.
If you wanted the first row to be 1 rather than 0, you might want to do this:
$CSV[(read-host "select row # from CSV")-1]
You could also drop the Return keyword entirely as it's not necessary. If you want to be more explicit, I personally favour Write-Output.
Just use the following method:
$counter = 0
$table = #()
XXX | foreach-object {
do something;
$table +=[pscustomobject]# {
XXXX
}
$table[$counter]
$counter++
}

PowerShell adds other values to return value of function

It seems that PowerShell adds an additional variable to the return value of a function.
The function subfoo2 itself delivers the correct values, but as soon as PowerShell jumps back to the postion where I called the function (in foo1), value contains the value of an other variable ($msg)
(Have a look at the comments in the code)
writeMessageLog($msg){
...
Add-Content $msg
...
}
subfoo2{
writeMessageLog($msg)
return $UserArrayWithValues #During Debug, $Array is fine (1)
}
foo1{
$var = subfoo2 $UserArray # $var has now the value of $msg and $UserArrayWithValues (2)
#do something with var
}
Realcode:
function WriteLog
{
param ( [string] $severity , $msgNumber, [string] $msg )
...
$msgOut = $date + ... + $msg
Add-Content $msgout ( $msgOut )
...
}
function getFeatures
{
writelog 'I' 1002 $true $true "Load Features"
$Features = importCsv -pPath $FeatureDefintionFilePath
Writelog 'I' 1000 $true $true "Features Loaded"
return $Features # $Features has value as expected (1)
}
function GetUserFeatures ($pUserObject)
{
$SfBFeatures = ""
$SfBFeatures = getFeatures #SfBFeaures has Value of $msg and $Features (2)
...
}
Do I use the functions/return values wrong? What could lead to such behavior? Is it an issue if i call a function within a function?
If I remove $msgOut = $date + ... + $msg in writeMessageLog, the values are fine.
I'm pretty lost right now, and have no ideas where this comes from. Any ideas welcome.
This is how powershell works, basically everything that you print out will be returned as the function output. So don't output extra stuff. To force something to not output stuff you can do:
$null = some-command_that_outputs_unwanted_things
since everybody is obsessed with Out-Null I'll add this link showing several other ways to do that.
Within a function, everything you don't assign or pipe to a consuming cmdlet will get put to the pipeline and returned from the function - even if you don't explicit return it. In fact the return keyword doesn't do anything in PowerShell so the following is equivalent:
function Test-Func
{
"Hello World"
}
function Test-Func
{
return "Hello World"
}
So it looks like your writeMessageLog puts anything on the pipeline thus you have to either assign the value to anything:
$notUsed = writeMessageLog($msg)
or (prefered) pipe it to the Out-Null cmdlet:
writeMessageLog($msg) | Out-Null

Function parameters order in PowerShell

I have this code in one of my PowerShell scripts:
function callCommandWithArguments([String] $arg1, [String] $arg2)
{
[string]$pathToCommand = "C:\command.exe";
[Array]$arguments = "anArg", "-other", "$arg2", "$arg1";
# the real code is
# & $pathToCommand $arguments;
# but was not working, so I change it to debug
Write-Host $pathToCommand $arguments;
}
callCommandWithArguments("1", "2");
As the arguments order is changed in the $arguments array, I would expect this output:
C:\command.exe anArg -other 2 1
But instead I receive a strange:
C:\command.exe anArg -other 1 2
Am I missing something obvious?
try call your function like this:
callCommandWithArguments "1" "2"
In powershell you pass arguments to function without () and just separated by space.
In your code you are passsing a single argument array of type object[]

Powershell getting rid of newlines in function

I'm trying to run a function that parses a large string that contains newlines, however whenever I pass this string into a function it gets rid of the new lines and makes it impossible to parse. Am I doing something wrong here?
function parseString([string] $s)
{
$result = $s | Select-String -pattern "foo"
return $result
}
If I type:
$s | Select-String -pattern "foo"
I get the correct result but using
parseString $s
returns the whole string with no newlines. Any suggestions?
EDIT: Hmm after messing around a bit I got rid of the [string] so it's
function parseString($s)
This seems to work, but why?
What is $s? If it is an array of string, then since you are saying that parseString takes in a string, the array of string is converted into a string. If on the other hand $s were a single string, it will work ( as shown below):
function parseString([string] $s)
{
$result = $s | Select-String -pattern "foo"
return $result
}
$s =#'
first line
second line with foo
third line
'#
parseString $s
But if $s=#("first line","secondline with foo","third line"), the array of strings is first converted to a string ( by simply joining each string ) and hence you will lose the newline. If you have got $s, from Get-Content etc. this will be the case.
Note that, most of the times, you won't need to specify the types in Powershell. Be it while assigning variables or in function paramaters.
PS:
If you did
$ofs = "`n"
parseString $s
you will get the expected result in the function with [string].
Removing [string] is probably removing the cast that is happening. You can cast object types to other types this way. For example:
$thing = [int] 42
$thing.GetType().FullName
Output: System.Int32
$thing = [string] $thing
$thing.GetType().FullName
Output: System.String
If your input is a string array, [string] will cast it to a single string, like the -join operator.
To see what is happing, print out $YourVariable.GetType().FullName before calling the function and inside the function (still using [string]).