First, use the array sub-expression operator @( ... ). This operator returns the result of one or more statements as an array. If there is only one item, the array has only one member.
$returnArray = @( Get-ChildItem "F:\PowerShell\A" )
Use Write-Output with the switch -NoEnumerate to prevent PowerShell from un-rolling the array.
Write-Output -NoEnumerate $returnArray
You can also effectively achieve the same result using Write-Output without using the -NoEnumerate by using the following syntax below.
Write-Output ,$returnArray
The comma in front of $returnArray effectively creates a new array with $returnArray as its only element. PowerShell then un-rolls this new single-element array, returning the original $returnArray intact to the caller.
Answer from Bob M on Stack OverflowFirst, use the array sub-expression operator @( ... ). This operator returns the result of one or more statements as an array. If there is only one item, the array has only one member.
$returnArray = @( Get-ChildItem "F:\PowerShell\A" )
Use Write-Output with the switch -NoEnumerate to prevent PowerShell from un-rolling the array.
Write-Output -NoEnumerate $returnArray
You can also effectively achieve the same result using Write-Output without using the -NoEnumerate by using the following syntax below.
Write-Output ,$returnArray
The comma in front of $returnArray effectively creates a new array with $returnArray as its only element. PowerShell then un-rolls this new single-element array, returning the original $returnArray intact to the caller.
try this one,
function Get-AlwaysArray
{
$returnArray = Get-ChildItem "F:\PowerShell\A"
Write-Output
returnArray
}
(Get-AlwaysArray).Gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Even after all my experience in PowerShell, sometimes I still get a little confused by arrays.
Assume the following basic helper function (other processing is done in my script once the contents are retrieved, but it boils down to this),
function Get-Thing {
Get-Content -Path C:\TEMP\somefile
}It's possible that file could be non-existent, empty, contain a single line, or contain multiple lines. I want it to ALWAYS return an array, be it empty, a single item, or the array of contents. I don't seem to be able to get it to do that however, even when specifying @(Get-Content -Path C:\TEMP\somefile) or array if there are no contents or a single item. The receiving function that calls this helper function always seems to get a string instead,
function Get-Thing {
@(Get-Content -Path C:\TEMP\somefile)
}
function Get-Thing {
[array](Get-Content -Path C:\TEMP\somefile)
}
# results in String
$thing = Get-Thing
# results in a proper array
$thing = @(Get-Content -Path C:\TEMP\somefile)
$thing = [array](Get-Content -Path C:\TEMP\somefile)How do I force the helper function to return an array? Or is it best practice to let it return what it will and handle the casting to an array in the receiving function? Thanks!
When you chain cmdlets together, the PowerShell pipeline wants/needs/is designed to feed "one thing at a time" down the pipeline, e.g.
Get-Content 'somefile' | Process-TextLine
Get-ChildItem | Process-Files
It doesn't matter what you do inside the function with @() and [array], it's the connection between functions where PowerShell will undo that and treat them as individual items again.
Which is usually what you want - it means you can use the pipeline, and | foreach-object {} and foreach ($thing in ____) {}, and if you just assign the results to a variable and there were multiple results, it will be an array.
The two main reasons you might want to force an array are:
-
The case the file was empty and you're getting
$nullbut want an empty array. This also normally works fine for pipeline and loops, but storing in a variable doesn't work. That's your@()and[array]in the outer, calling, function where you store the results. This is (AFAIK) good practice to do it like this and not try to do it from inside the function. -
The case where you want to group things together and send (3 lines) down the pipeline all together.
e.g.
PS D:\> @(1,2,3),@(4,5,6) | foreach { "-$_-" }
-1 2 3-
-4 5 6-
And when there is only one of those, it falls over:
@(1,2,3) | ForEach { "-$_-" }
-1-
-2-
-3-
This is the case where you need u/TalkToTheFinger's solution of putting a comma in front, and all this does is force it to be like an array inside an array:
PS D:\> @(,@(1,2,3)) | ForEach { "-$_-" }
-1 2 3-
It looks a bit weird, like you should be able to write @(@(1,2,3)) or @(@(1,2,3), ) or @($null, @(1,2,3)) but they are all not quite the same, to get it right and make it work needs one comma in front.
The above behaviour still happens - the array is unpacked and each item is sent down the pipeline, it's just that each item is now "an array of 3 items".
There are two cases here - if you want [array]$thing = get-content 'filename' to force an array even if there's only one or no lines, do it that way.
If you try to force it with the comma, it will break in the normal case of lots of lines and they will all be sent together. i.e. it works to force an array in the case of 0 or 1 result, but it breaks in the normal case of lots of results - instead of "an array of results" you have "an array of array of results". If you want to write your own get-thing and you try to force it with ,$lines what you'll get is "an array of (an array of lines)" and that will be silly.
But sometimes you do want that "several arrays together, don't flatten them" behaviour, you can force it, and they will stay together down through the pipeline.
Adding a comma in front should preserve the array even if it's empty, e.g. function Get-Thing { ,@(Get-Content -Path C:\TEMP\somefile) }
Powershell - Pass array from one function to another
Strange Issue With Powershell Function Returning Values
How to return several items from a Powershell function - Stack Overflow
How can I force Powershell to return an array when a call only returns one object? - Stack Overflow
OK, shelf that. It’s still about scope, though. Here’s a test that worked:
function firstFunction {
$array = 1,2,3
return $array
}
function secondFunction {
$array = firstFunction
Write-Host $array
}
secondFunction
The problem was $array was defined in the firstFunction scope and you were trying to reference it in the secondFunction scope, even though you “returned” it all you really do is return the value, not the variable itself. So $array dies as soon as you exit firstFunction.
So I simply assigned the returned value to a new variable (that happened to also be called $array, but it’s not the same variable) and write it. However, if you type Write-Host $array after the secondFunction function call, you’ll get a $null!
secondFunction
Write-Host "...$array..."
Again, the $array variable dies with the function. You could Return it (Write-Output also works) and assign the original call to a variable:
$Final = secondFunction
Then everything would come out of the functions intake.
All right, so the basics:
I have a 1 button, the first button calls a function which returns an array.
I have another button that needs to use the array from the first function.
I’ve tried calling the first function from the second function and have a return statement for my array. The code for this is pretty long, but in a general sense it’s just how to I return an array from one function and then use it in a second function. I’ve been Googleing all yesterday and haven’t found anything. Below is a short example of what I’m trying to do…
function firstFunction {
$array = 1,2,3
return ,$array
}
function secondFunction {
firstFunction
Write-Host $array
}
Second function doesn’t return anything in array from the first function. Any ideas?
Hmm well I made a little test and that actually works…now why doesn’t my script work I’ve no idea…I’ll include my functions below…
function getUserLogon
{
$logonfile = "C:\Users\benw\Documents\PowershellScripts\Development\UserTracking\logon*.txt"
$logonfile2 = "C:\Users\benw\Documents\PowershellScripts\Development\UserTracking\archive\*.txt"
$outputBox.clear()
$username = $UserComboBox.SelectedItem.ToString()
$startdate = $StartDateBox.text
$enddate = $EndDateBox.text
$data = @(get-content $logonfile,$logonfile2| select-string $username)
$array = @()
$start = [datetime]::parseexact($startdate,"MM/dd/yyyy",$null)
$end = [datetime]::parseexact($enddate,"MM/dd/yyyy",$null)
if($start.length -gt 0 -and $end.length -gt 0)
{
if($start -le $end)
{
foreach($line in $data)
{
line
$var = [string]
var = $var.substring(4,10)
$var2 = [datetime]::parseexact($var,"MM/dd/yyyy",$null)
if(($var2) -ge ($startdate) -and ($var2) -le ($enddate))
{
$array += $line
}
}
if($array.length -gt 1)
{
$outputBox.text = $array | out-string
}
else
{
$outputBox.text = "No matches found"
}
}
else
{
<#
foreach($line in $data)
{
line
$var = [string]
var = $var.substring(4,10)
$var2 = [datetime]::parseexact($var,"MM/dd/yyyy",$null)
if(($var2) -le ($startdate) -and ($var2) -ge ($enddate)) #-ge before date -le after date -eq same date
{
$array += $line
}
}
if($array.length -gt 1)
{
$outputBox.text = $array | out-string
}
else
{
$outputBox.text = "No matches found"
}
#>
$outputBox.text = "Ensure the Start date is before the End date."
}
}
elseif ($start.length -gt 0 -or $end.length -gt 0)
{
foreach($line in $data)
{
line
$var = [string]
var = $var.substring(4,10)
$var2 = [datetime]::parseexact($var,"MM/dd/yyyy",$null)
if(($var2) -eq ($startdate) -or ($var2) -eq ($enddate)) #-ge before date -le after date -eq same date
{
$array += $line
}
}
if($array.length -gt 1)
{
$outputBox.text = $array | out-string
}
else
{
$outputBox.text = "No matches found"
}
}
else
{
$outputBox.text = "Please select a Start and End Date"
}
return ,$array
}
And my second function…
function exportCsvResults
{
getUserLogon
Write-Host $array
}
Which writes out nothing…
I agree with @Christian, and I add another solution.
First you can return using an array explicitly or implicitly:
explicitly
function ExplicitArray () { $myArray = @() $myArray += 12 $myArray += "Blue" return ,$myArray } Clear-Host $a = ExplicitArray Write-Host "values from ExplicitArray area[0]) and
a[1])"
implicitly
function ImplicitArray () { Write-Output 12 Write-Output "Blue" return "green" } $b = ImplicitArray Write-Host "values from ImplicitArray areb[0]),
b[1]) and
b[2])"
Second you can return a custom object:
Short form
function ReturnObject () { $value = "" | Select-Object -Property number,color $value.Number = 12 $value.color = "blue" return $value } $c = ReturnObject Write-Host "values from ReturnObject arec.number) and
c.color)"
School form
function SchoolReturnObject () { $value = New-Object PsObject -Property @{color="blue" ; number="12"} Add-Member -InputObject $value –MemberType NoteProperty –Name "Verb" –value "eat" return $value } $d = SchoolReturnObject Write-Host "values from SchoolReturnObject ared.number),
d.color) and
d.Verb)"
Third using argument by reference:
function addition ([int]
y, [ref]$R)
{
$Res =
y
$R.value = $Res
}
$O1 = 1
O3 = 0
addition
O2 ([ref]$O3)
Write-Host "values from addition
o2 is $o3"
Maybe I am misunderstanding the question but isn't this just as simple as returning a list of variables, and the caller simply assigns each to a variable. That is
> function test () {return @('a','c'),'b'}
>
b = test
$a will be an array, and $b the letter 'b'
>
b
b
Define the variable as an array in one of two ways...
Wrap your piped commands in parentheses with an @ at the beginning:
$serverIps = @(gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort)
Specify the data type of the variable as an array:
[array]$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
Or, check the data type of the variable...
IF ($ServerIps -isnot [array])
{ <error message> }
ELSE
{ <proceed> }
You can either add a comma(,) before return list like return ,$list or cast it [Array] or [YourType[]] at where you tend to use the list.