Return Multiple Powershell Variables

elijah eccher
elijah eccher used Ask the Experts™
on
Hi All,

I'm creating a "Loop-caller" cmdlet. supposed to be super simple but I cannot get around how to pass these variables.

The idea is that as an Administrator I often run into little jobs like "check all computers for XXX" where xxx varies anywhere from IP, MAC, installed program, driver version ETC.
I've written tiny modular cmdlets for finding such things.

The idea of this "Loop-caller" is to take a txt file of computernames and call the function for each one, then I want the loop-caller to put the returned data from the info cmdlet into a hash table.

Example:

# Up-Info : This program asks the use for a target computer and then queries that computer for uptime.
param(
    [Parameter(Mandatory)]$ComputerName
)
$computer = get-fqdn $ComputerName          # calls my cmdlet "get-fqdn" which scrapes ping -a -n 1 for a machine's FQDN (our NW has more than one domain.)
$bootTime = (get-date) - (gcim -ComputerName $computer -ClassName win32_operatingsystem).LastBootUpTime
return $computer, $bootTime

# This Returns 2 Variables $computer and $bootTime

# Loop Caller
Param(
    [Parameter(Mandatory)]${Where is the List.txt?},
    [Parameter(Mandatory)]${Which Program are we Looping?}
)
$lsfile =cat ${Where is the List.txt?}
$loopCMD = ${Which Program are we Looping?}
$keys = @{}
for($i=0;$i -lt $lsfile.count;$i++){
   $keys.add{powershell.exe $loopCMD $lsfile[$i]}
}
# This function works great without the $keys = @{} and the $keys.add{}  
# it calls the function and passes the computername which is properly read and the output is displayed on screen.
# I want to store the output as an object instead I would like a hash Table with $computer = $bootTime Where Loop-Caller is creating the hash table as it gets the return values.

is there a way to return 2 variables at once without dumping them into a single string and using split? I don't want to throw away that much information!
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
IT Assistant
Commented:
Hey guys!

Naturally all I had to do was ask for help in order to find the answer myself.

The solution is not to add keys directly from the call like this:
$keys.add{powershell.exe $loopCMD $lsfile[$i]}

But instead  to load and object variable with the output and then put that variable into the hash table.
  $returnVals = powershell.exe $loopCMD $lsfile[$i]
   $keys.add($returnVals[1], $returnVals[4..8] -replace " ")

This is what I am using and it works for my purposes.
If someone comes up with a simpler solution I would love to hear it! Simple code is fast code.
Jose Gabriel Ortega CastroTop Rated Freelancer on MS Technologies
Awarded 2018
Distinguished Expert 2018

Commented:
Indeed when you want to return several values, save them into a variable (or class if ps version 5.0 or higher)
I'll let you this one :

https://devblogs.microsoft.com/scripting/powershell-5-create-simple-class/

Take care!
Most Valuable Expert 2018
Distinguished Expert 2018

Commented:
Some general comments first:
* Use <Verb>-<SingularNoun> syntax for function names, with <Verb> coming from the list you get by Get-Verb (or https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands?view=powershell-5.1).
* Always use the full cmdlet/function names in scripts, not aliases; use aliases only in quick interactive command lines, where you know what the alias actually does.
* Function arguments should be easy to discover and have common names; when you need a path to a file as input, use "Path", not "Where is the List.txt?"
* You don't need to create new instances of PowerShell just to run a PS script; just use the & operator and the path to the script.

Now, about returning results: the easiest way is to create custom objects on the fly.
I've moved the computer list processing into the program; there's no need for the caller to handle that.
# Up-Info : This program asks the use for a target computer and then queries that computer for uptime.
Param(
	[Parameter(Mandatory=$true)]
	[String[]]$ComputerName
)
	ForEach ($computer in $ComputerName) {
		$fqdn = Get-Fqdn $computer          # calls my cmdlet "get-fqdn" which scrapes ping -a -n 1 for a machine's FQDN (our NW has more than one domain.)
		[PSCustomObject]@{
			'ComputerName' = $fqdn
			'BootTime' = (Get-Date) - (Get-CimInstance -ComputerName $fqdn -ClassName Win32_OperatingSystem).LastBootUpTime
		}
	}

# Loop Caller
Param(
	[Parameter(Position=0, Mandatory=$true, HelpMessage='Where is the List.txt?')]
	[String]$Path,
	[Parameter(Position=1, Mandatory=$true, HelpMessage='Which Program are we Looping')]
	[String]$Command
)
	$computerNames = Get-Content -Path $Path -ErrorAction Stop
	$customObjects = & $Command -ComputerName $computerNames

Open in new window

Or, if you insist on the hashtable, try it like this:
# Up-Info : This program asks the user for a target computer and then queries that computer for uptime.
Param(
	[Parameter(Mandatory=$true)]
	[String[]]$ComputerName
)
	$return = @{}
	ForEach ($computer in $ComputerName) {
		Write-Host "Processing $($computer)"
		$fqdn = Get-Fqdn $computer          # calls my cmdlet "get-fqdn" which scrapes ping -a -n 1 for a machine's FQDN (our NW has more than one domain.)
		$return[$fqdn] = (Get-Date) - (Get-CimInstance -ComputerName $computer -ClassName Win32_OperatingSystem).LastBootUpTime
	}
	$return 


# Loop Caller
Param(
	[Parameter(Position=0, Mandatory=$true, HelpMessage='Where is the List.txt?')]
	[String]$Path,
	[Parameter(Position=1, Mandatory=$true, HelpMessage='Which Program are we Looping?')]
	[String]$Command
)
	$computerNames = Get-Content -Path $Path -ErrorAction Stop
	$hashtable = & $Command -ComputerName $computerNames

Open in new window

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial