I am working on PowerShell script that will check server services. I keep getting a "Cannot index into a null array."
The error references the second if statement "if ($select_string_result.Line[$select_string_result.Line.Length-1] -eq '1')"
The object type of "$select_string_result" is displayed as Array and the txt file has data but the script will not process through it.
The "Line" property of the array records as null and the "Length" is recorded as 0.
$filepathserver = 'Path'
$filepathlocal = 'Path'
function Get-Timestamp
{
return Get-Date -Format "MM/dd/yyyy hh:mm:ss tt"
}
function refresh-data
{
# Pulls Services and Services Status
$orionData = Get-SwisData $swis "SELECT ComponentID, StatusDescription FROM Orion.APM.Component"
# Sends output to a txt file
$orionData | Out-File "$filepathlocal\All_App_Services.txt"
}
function check-status($select_string_result)
{
if ($select_string_result.Line -isnot [system.array])
{
if ($select_string_result.Line[$select_string_result.Line.Length-1] -eq '1')
{
return 100
}
else
{
return 0
}
}
else
{
$sum = 0.0
$add = 100.0/$select_string_result.Length
foreach ($match in $select_string_result)
{
if ($match.Line[$match.Line.Length-1] -eq '1')
{
$sum += $add
}
}
if ($sum -lt 100) {$sum = 0} # this line collapses the values in to either 0 or 100
$sum = [int][Math]::Ceiling($sum)
return $sum
}
}
function main
{
refresh-data
# Filters for Application specific Services
$f = #("94944 ", "94945 ", "94951 ", "94946 ", "94942 ", "94948 ", "94949 ", "94950 ", "94943 ", "94947 ", "94952 ", "94953 ")
$AppServices = Get-Content "Path" | Select-String $f
$AppServices | Set-Content "Path"
#Removes leading spaces from array
(Get-Content "$filepathlocal\File.txt") -replace "Up","1" | % {$_.trim()} | Out-File "$filepathlocal\File.txt"
$AppServices = Get-Content "$filepathlocal\File.txt"
$AppServices.GetType()
# Writes status of each group to .txt file
$logfile= "$filepathserver\ServicesStatus.txt"
$t = Get-Timestamp
$v = check-status $AppServices
$s = "$t|Application-Services|$v"
$s | Out-File $logfile -Append -Encoding "UTF8"
$s
}
main
$select_string_result.Line resolves to $null because the array of strings that you get from Get-Content does not have a Line property, so the if statement should look more like:
if($select_string_result[$select_string_result.Length - 1] -eq '1') { ... }
PowerShell also allows you to address the last index with just -1, allowing us to simplify the statement as:
if($select_string_result[-1] -eq '1') { ... }
That being said, rather than attempting to check whether the parameter passed to a function is an array or not, you'd want to declare the parameter an array in the first place and then use a foreach loop over it:
function check-status([string[]]$select_string_result)
{
$sum = 0.0
$add = 100.0/$select_string_result.Length
foreach ($match in $select_string_result)
{
if ($match[-1] -eq '1')
{
$sum += $add
}
}
if ($sum -lt 100) {$sum = 0} # this line collapses the values in to either 0 or 100
$sum = [int][Math]::Ceiling($sum)
return $sum
}
much nice, way less code.
Now, instead of attempting to index into the string, I'd suggest using the -like wildcard operator or the -match regex operator to check whether each string ends with 1:
if ($match -like '*1')
{
$sum += $add
}
Since $sum is always exactly 100, or otherwise gets reset to 0, the call to [Math]::Ceiling() is redundant and can be removed:
function check-status([string[]]$select_string_result)
{
$sum = 0.0
$add = 100.0/$select_string_result.Length
foreach ($match in $select_string_result)
{
if ($match -like '*1')
{
$sum += $add
}
}
if ($sum -lt 100) {$sum = 0} # this line collapses the values in to either 0 or 100
return $sum
}
If you look carefully at the function as implemented, you'll notice that the only case in which 100 is returned is when all strings in $select_string_result end in 1.
We can easily test for this by using the -like operator directly on our input array, it will act as a filter operator:
function check-status([string[]]$select_string_result)
{
if(#($select_string_result -like '*1').Count -eq $select_string_result.Count)
{
$sum = 100
}
else
{
$sum = 0
}
return $sum
}
Now, another way of asserting that all strings in the array end in 1, is to simply test whether no string does not end in 1:
function check-status([string[]]$select_string_result)
{
if(#($select_string_result -notlike '*1'))
{
$sum = 0
}
else
{
$sum = 100
}
return $sum
}
Now all we need to do is shine it up a bit, like change check to a more appropriate verb and we've got a much nicer, short or powershell-idiomatic function :-)
function Measure-StatusValue
{
param(
[ValidateNotNullOrEmpty()]
[string[]]$StatusStrings
)
if(#($StatusStrings -notlike '*1'))
{
return 0
}
return 100
}
Related
I need help optimizing my PowerShell script.
$sorted = #()
$firsttime = 0
$j = 0
$zaehler = $results.Count-1
for ($i=0; $i -le $results.Count-1; $i++) {
$j = $i+1
while ($results.GUID[$i] -eq $results.GUID[$j]) {
$klassen = ""
$rec = $results | where {$_.GUID -eq $results.GUID[$i]}
if ($firsttime -eq 0 -or !$sorted.GUID.contains($rec[0].GUID)) {
$firsttime = 1
foreach ($item in $rec.Klasse) {
if ($klassen -eq "") {
$klassen += $item
} else {
if (!$klassen.Contains($item)) {
$klassen += "," + $item
}
}
}
$rec[0].Klasse = $klassen
$sorted += $rec[0]
}
$j = $j+1
}
Write-Host ($i/$zaehler).ToString("P") "von Schule" $schule
}
if (!$sorted) {
$results
} else {
$sorted
}
Basically in my resultset ($results) I got duplicate lines of teachers and the only difference is the class ("Klasse/Klassen") they are teaching at.
To minimize the output I am checking if the first GUID is the same as the second and then the script appends the second class to the first one. So the $sorted array has just one line per teacher with a comma-seperated string which shows all classes.
Sample line of $results:
#{
GUID={1234567-1234-1234-1234-1234567};
userrole=teacher;
Vorname=Max;
Nachname=Mustermann;
Geburtstag=01.01.2000;
Klasse=9A;
Schule=123456
}
#{
GUID={1234567-1234-1234-1234-1234567};
userrole=teacher;
Vorname=Max;
Nachname=Mustermann;
Geburtstag=01.01.2000;
Klasse=9B;
Schule=123456
}
Sample line of $sorted[0]:
#{
GUID={1234567-1234-1234-1234-1234567};
userrole=teacher;
Vorname=Max;
Nachname=Mustermann;
Geburtstag=01.01.2000;
Klasse=9A,9B,9C,5A;
Schule=123456
}
The sorting process (check if contains, foreach $item, add to $klassen) is pretty slow.
I would be very grateful for any kind of ideas how to optimize the script.
Maybe something like this would work:
$results | ForEach-Object {
New-Object -Type PSObject -Property $_
} | Group-Object GUID | ForEach-Object {
$teacher = $_.Group
$teacher | Select-Object -First 1 -Exclude Klasse -Property *, #{n='Klasse';e={
$teacher | Select-Object -Expand Klasse
}}
}
Convert your hashtables into custom objects, group them by GUID, then replace the original "Klasse" property with a new one containing an array of the values from all objects in that group, and select the first result.
Should $results already be a list of objects you can skip the first ForEach-Object:
$results | Group-Object GUID | ForEach-Object {
$teacher = $_.Group
$teacher | Select-Object -First 1 -Exclude Klasse -Property *, #{n='Klasse';e={
$teacher | Select-Object -Expand Klasse
}}
}
Before calling a recursive function (RecursiveWebs()) $webinfo.Count is 1. During the execution of recursive function, Count is increased, but as soon as the recursive function is completed and control returns to the main function, $webinfo.Count again decreases to 1.
function RecursiveWebs($web) {
$Properties = #{
Title = $web.Title
URL = $web.Url
}
$webinfo += New-Object PSObject -Property $properties
foreach ($w in $web.Webs) {
if ($w.Webs.Count -gt 0) {
RecursiveWebs $w
}
}
}
$global:webinfo = #()
$sites = Get-SPOSite -Limit All
foreach ($site in $sites) {
Write-Host $site.Url
try {
$Properties = #{
Title = $site.Title
URL = $site.Url
NoOfSubsites = $site.Webs.Count
}
$webinfo += New-Object PSObject -Property $properties
if ($site.Webs.Count -gt 0) {
RecursiveWebs $site
}
} catch {
Write-Host $_ -ForegroundColor Red
}
}
Replaces $webinfo with $Global:webinfo everywhere in the code and it did the trick
I'm trying to use the below script to iterate through an array that is built using the contents of a text file and return the matching value, or the argument if no match is found.
Param (
[string]$surname
)
$listSurnames = #(Get-Content .\MixedCaseNames.TXT)
# Return $listSurnames
$found = for($index = 0; $index -lt $listSurnames.Count; $index++) {
if ($listSurnames[$index] -eq $surname) { return $listSurnames[$index]; break }
else { return $surname; break }
}
Excellent! Thank you all. :)
Param (
[string]$surname
)
$findName = Get-Content .\MixedCaseNames.TXT | Where-Object { $_ -eq $surname } | Select-Object -First 1
If ( $findName -eq $null ) { return $surname } Else { return $findName }
I'm trying to figure out how to simplify this process, but it's not as simple as I thought.
I have a config file that looks similar to this:
[string][1][options]
$List = #(
"c:\path\to\file,1,-a,-b,-c,-d,-e"
)
The only items required are the [string] and the [1]. There are 10 options (-a, -b etc), potentially more.
Each of which is optional and could be supplied in any order.
In the main script I then do the following at present:
foreach ($a in $List) {
$dataSplit = $a -split"(,)"
$string = $dataSplit[0]
$number = $dataSplit[2]
$ds4 = $dataSplit[4]
if(!$ds4) {
$ds4 = "0"
} elseif($ds4.StartsWith("-a")) {
$a_set = 1
write-host "a_set has been set to $a_set"
} elseif($ds4.StartsWith("-b")) {
$b_set = 1
write-host "b_set has been set to $b_set"
}
. . .
if(!$ds5) {
$ds5 = "0"
}
. . .
As you can imagine this gets quite long. So I thought I would simplify it with a function. e.g.
function get-additional($item) {
if($item.StartsWith("-a")) {
$a_set = 1
Write-Host "$a_set has been set"
return $a_set
}
if($item.StartsWith("-b")) {
$b_set = 1
Write-Host "$b_set has been set"
return $b_set
}
}
And then call it thus:
if(!$ds4) {
$ds4 = "0"
} else {
get-additional($ds4)
}
Is there a way to do this? I've seen pleanty of examples if you only have a single variable to return, or even a fixed number, but none that allow for the return of 'one of many' variables.
Here is the (shortened) script in one if it helps:
$List = #(
"c:\path\to\file,1,-b,-c,-d,-e"
)
function get-additional($item) {
if($item.StartsWith("-a")) {
$a_set = 1
Write-Host "a_set has been set to $a_set"
return $a_set
}
if($item.StartsWith("-b")) {
$b_set = 1
Write-Host "b_set has been set to $b_set"
return $b_set
}
}
$a_set = 0
$b_set = 0
$c_set = 0
foreach ($a in $List) {
$dataSplit = $a -split"(,)"
$string = $dataSplit[0]
$number = $dataSplit[2]
$ds4 = $dataSplit[4]
Write-Host "ds4 = $ds4"
if(!$ds4) {
$ds4 = "0"
} else {
get-additional($ds4)
}
$ds5 = $dataSplit[6]
Write-Host "ds5 = $ds5"
if(!$ds5) {
$ds5 = "0"
} else {
get-additional($ds5)
}
}
Write-Host "a = $a_set"
Write-Host "b = $b_set"
The desired result at the end would be
a = 0
b = 1
- - - UPDATE 2015-11-30 16:54
In case it helps to understand what I am going for here's a Sample from my actual script
$cfg_AppList = #(
"C:\Path\to\application1\app1.exe instance1,1"
"C:\Path\to\application2\app2.exe instance2,1,-p12345"
"C:\Path\to\application3\app3.exe instance3,0"
"C:\Path\to\application3\app3.exe instance3,1,-p78901"
)
function get-additional($item)
{
$script:pval = "0"
if($item.StartsWith("-p"))
{
$script:pval = $ds4.substring(2)
write-host "$pval is a pval"
}
}
$AppObject = #()
foreach($a in $cfg_AppList)
{
$dataSplit = $a -split","
$AppVal = $dataSplit[0]
$checkVal = $dataSplit[1]
$ds4 = $dataSplit[2]
if(!$ds4)
{
$ds4 = "0"
}
else
{
get-additional($ds4)
}
$AppObject += New-Object PSObject -property #{
AppVal = "$AppVal";
checkVal = "$checkVal";
pval = "$pval";
}
}
The $AppObject object is then referenced and updated as the script progresses.
The values supplied in pval and (see below eval) will determine what happens.
I now need to add a second element -e which will be included thus:
$cfg_AppList = #(
"C:\Path\to\application1\app1.exe instance1,1"
"C:\Path\to\application2\app2.exe instance2,1,-p12345"
"C:\Path\to\application3\app3.exe instance3,0,-e"
"C:\Path\to\application3\app3.exe instance3,1,-e,-p78901"
)
It will be either selected 1 or not selected 0, and added to the $AppObject Array as eval=$eval (1|0).
Going forward I have more options I plan to introduce, hence the need to find the most efficient way to handle them all.
- - - UPDATE 2015-12-01 11:39
OK, What I have gone with is a combination of both ideas below.
Placing the options into an array and looping through them, then using a SWITCH statement to see which ones are set.
$AppObject = #()
foreach($a in $cfg_AppList)
{
$pval = 0
$eval = 0
$AppVal,$CheckVal,$options = $a -split","
foreach($opt in $options)
{
switch -wildcard ($opt)
{
'-p*' { $pval = $opt.substring(2) }
'-e' { $eval = 1 }
}
}
$AppObject += New-Object PSObject -property #{
AppVal = "$AppVal";
CheckVal = "$CheckVal";
pval = "$pval";
eval = "$eval";
}
}
First off, don't capture the , in your split operation if you're not planning to use it for anything, just use -split "," (no parentheses).
We can make use of multiple variable assignment to "shift" away to string and number 1:
$s,$n,$opts = "string,1,-a,-b,-c" -split ","
$opts will now contain the string array: #("-a","-b","-c")
The easiest way to check for whether a predetermined set of options is present or not, is to simply loop through all possible options and see if they are contained in the input string:
function Parse-InputString
{
param($InputString)
# prepare the options you want to check for
$PossibleOptions = "abcde".ToCharArray()
# Split the input string
$String,$Number,$Options = $InputString -split ","
# Create a new object with the string and number values
$OutputObject = New-Object psobject -Property #{
"String" = $String
"Number" = $Number
}
# Now inspect the $Options array to see if any of them are set
foreach($PossibleOption in $PossibleOptions){
$OptionSet = if($Options -contains "-$PossibleOption"){
1
} else {
0
}
# Add the information to the object
$OutputObject |Add-Member -MemberType NoteProperty -Name $PossibleOption -Value $OptionSet
}
# return the object carrying all the information
return $OutputObject
}
Now you can have your input string parsed nicely into an actual object:
PS C:\> Parse-InputString -InputString "c:\path\to\file,1,-b,-c,-d,-e"
Number : 1
String : c:\path\to\file
a : 0
b : 1
c : 1
d : 1
e : 1
The easiest way would be to update the global variables in your function without returning anything:
function Get-Additional($item) {
if ($item.StartsWith("-a")) {
$global:a_set = 1
Write-Host "a_set has been set to $a_set"
}
if ($item.StartsWith("-b")) {
$global:b_set = 1
Write-Host "b_set has been set to $b_set"
}
}
However, modifying global variables in functions is not a good practice, because it's difficult to debug. I wouldn't recommend going this route.
A better approach is to pass your current values as parameters into the function, return the modified values, and assign them back to variables.
function Get-Additional($item, $a, $b) {
if ($item.StartsWith("-a")) {
$a = 1
Write-Host "a_set has been set to $a_set"
}
if ($item.StartsWith("-b")) {
$b = 1
Write-Host "b_set has been set to $b_set"
}
#($a, $b)
}
$set_a, $set_b = Get-Additional $ds4 $set_a $set_b
In the above sample the function returns a list of the modified values (#($a, $b)), which are then assigned back to the list $set_a, $set_b. The return keyword is not required for returning something from a PowerShell function. It controls only where to return from a function, not what to return.
With that said, for your scenario I wouldn't use a function in the first place. A switch statement would be better suited for this kind of manipulation:
switch -wildcard ($ds4) {
'-a*' { $set_a = 1 }
'-b*' { $set_b = 1 }
}
I'm currently trying to make a function that gets all possible combinations of array values.
I have come up with a non function version but it's limited to 3 values so i'm trying to make a function out of it to become more Dynamic
I tried searching SO but could not find a powershell example of what i was trying to do, i could find a PHP version but i'm very limited in my PHP
PHP: How to get all possible combinations of 1D array?
Non-function Script
$name = 'First','Middle','Last'
$list = #()
foreach ($c1 in $name) {
foreach ($c2 in $name) {
foreach ($c3 in $name) {
if (($c1 -ne $c2) -and ($c2 -ne $c3) -and ($c3 -ne $c1))
{
$list += "$c1 $c2 $c3"
}
}
}
}
This gives me the result
First Middle Last
First Last Middle
Middle First Last
Middle Last First
Last First Middle
Last Middle First
I'm not sure how i would rearrange the values when i'm recursing the function, this is what i have so far:
<#
.Synopsis
Short description
.DESCRIPTION
Long description
.EXAMPLE
Example of how to use this cmdlet
.EXAMPLE
Another example of how to use this cmdlet
#>
function Get-Combinations
{
[CmdletBinding()]
[OutputType([int])]
Param
(
# Param1 help description
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string[]]$Array,
# Param1 help description
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$false,
Position=1)]
[string]$Temp,
# Param1 help description
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$true,
Position=2)]
[string[]]$Return
)
Begin
{
Write-Verbose "Starting Function Get-Combinations with parameters `n`n$($Array | Out-String)`n$temp`n`n$($Return | Out-String)"
If ($Temp)
{
$Return = $Temp
}
$newArray = new-object system.collections.arraylist
}
Process
{
Write-Verbose ($return | Out-String)
For($i=0; $i -lt $Array.Length; $i++)
{
#Write-Verbose $i
$Array | ForEach-Object {$newArray.Add($_)}
$newArray.RemoveAt($i)
Write-Verbose ($newArray | Out-String)
if ($newArray.Count -le 1)
{
Get-Combinations -Array $newArray -Temp $Temp -Return $Return
}
else
{
$Return = $Temp
}
}
$newArray
}
End
{
Write-Verbose "Exiting Function Get-Combinations"
}
}
$combinations = #("First","First2","Middle","Last")
$Combos = Get-Combinations -Array $combinations
$Combos
But the output i'm getting is all over the place
First2
Last
First2
Last
First
First2
Middle
Last
First
First2
Middle
Last
28/08 Update
Getting closer but still getting weird output
<#
.Synopsis
Short description
.DESCRIPTION
Long description
.EXAMPLE
Example of how to use this cmdlet
.EXAMPLE
Another example of how to use this cmdlet
#>
function Get-Combinations
{
[CmdletBinding()]
[OutputType([int])]
Param
(
# Param1 help description
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string[]]$Array,
# Param1 help description
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$false,
Position=1)]
[string]$Temp,
# Param1 help description
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$true,
Position=2)]
[string[]]$Return
)
Begin
{
Write-Verbose "Starting Function Get-Combinations with parameters `n`n$($Array | Out-String)`n$temp`n`n$($Return | Out-String)"
If ($Temp)
{
$Return += $Temp
}
#$newArray = new-object [System.Collections.ArrayList]
#$Array | ForEach-Object {$newArray.Add($_) | Out-Null}
[System.Collections.ArrayList]$newArray = $Array
}
Process
{
Write-Verbose "return -> $return"
For($i=0; $i -lt $Array.Length; $i++)
{
Write-Verbose "`$i -> $i"
$element = $newArray[0]
$newArray.RemoveAt(0)
Write-Verbose "`$newArray -> $newArray"
Write-Verbose "Element -> $element"
if ($newArray.Count -gt 0)
{
Get-Combinations -Array $newArray -Temp (($temp + " " +$element).Trim()) -Return $Return
}
else
{
$Return = $Temp + " " + $element
}
}
$return
}
End
{
Write-Verbose "Exiting Function Get-Combinations"
}
}
$combinations = #("First","First2","Middle","Last")
$return = #()
$Combos = Get-Combinations -Array $combinations -Return $return
$Combos
New output (Yes there is a space before the 'Last' value, no i have no idea why)
First First2 Middle Last
First First2 Last
First Middle Last
First Last
First2 Middle Last
First2 Last
Middle Last
Last
Here is my solution:
function Remove ($element, $list)
{
$newList = #()
$list | % { if ($_ -ne $element) { $newList += $_} }
return $newList
}
function Append ($head, $tail)
{
if ($tail.Count -eq 0)
{ return ,$head }
$result = #()
$tail | %{
$newList = ,$head
$_ | %{ $newList += $_ }
$result += ,$newList
}
return $result
}
function Permute ($list)
{
if ($list.Count -eq 0)
{ return #() }
$list | %{
$permutations = Permute (Remove $_ $list)
return Append $_ $permutations
}
}
cls
$list = "x", "y", "z", "t", "v"
$permutations = Permute $list
$permutations | %{
Write-Host ([string]::Join(", ", $_))
}
EDIT: the same in one function (Permute). This is cheating a bit, however since I replaced plain functions whith lambdas. You could replace recursive calls with a stack you handle yourself, but that would make the code unecessarily complex ...
function Permute ($list)
{
$global:remove = {
param ($element, $list)
$newList = #()
$list | % { if ($_ -ne $element) { $newList += $_} }
return $newList
}
$global:append = {
param ($head, $tail)
if ($tail.Count -eq 0)
{ return ,$head }
$result = #()
$tail | %{
$newList = ,$head
$_ | %{ $newList += $_ }
$result += ,$newList
}
return $result
}
if ($list.Count -eq 0)
{ return #() }
$list | %{
$permutations = Permute ($remove.Invoke($_, $list))
return $append.Invoke($_, $permutations)
}
}
cls
$list = "x", "y", "z", "t"
$permutations = Permute $list
$permutations | %{
Write-Host ([string]::Join(", ", $_))
}
I tried to learn something new and help you out but Im stuck. maybe this will help you get in the right direction but I dont know enough about Powershell recursion to figure this out. I converted the php to powershell and in theory it should work but it doesnt.
$array = #('Alpha', 'Beta', 'Gamma', 'Sigma')
function depth_picker([system.collections.arraylist]$arr,$temp_string, $collect)
{
if($temp_string -ne ""){$collect += $temp_string}
for($i = 0; $i -lt $arr.count;$i++)
{
[system.collections.arraylist]$arrCopy = $arr
$elem = $arrCopy[$i]
$arrCopy.removeRange($i,1)
if($arrCopy.count -gt 0){
depth_picker -arr $arrCopy -temp_string "$temp_string $elem" -collect $collect}
else{$collect += "$temp_string $elem"}
}
}
$collect = #()
depth_picker -arr $array -temp_string "" -collect $collect
$collect
It seems to work and will get you the first set of possibles:
Alpha
Alpha Beta
Alpha Beta Gamma
Alpha Beta Gamma Sigma
But for some reason that I cant figure out when it gets back to the previous functions and does $i++ then checks ($i -lt $arr.count) $arr.count it always 0 so it never goes to the next iteration to continue finding the possibilities.
Hopefully someone else can fix what I cant seem to figure out as I dont know enough about recursion. But it seems that with each level of depth called the previous depth level $arr variable and values is lost.
Here is my solution with a recursive function. It generates space separated strings but it's quite simple to split each element with $list[$i].split(" "):
function Get-Permutations
{
param ($array, $cur, $depth, $list)
$depth ++
for ($i = 0; $i -lt $array.Count; $i++)
{
$list += $cur+" "+$array[$i]
if ($depth -lt $array.Count)
{
$list = Get-Permutations $array ($cur+" "+$array[$i]) $depth $list
}
}
$list
}
$array = #("first","second","third","fourth")
$list = #()
$list = Get-Permutations $array "" 0 $list
$list
The solution posted by Micky Balladelli almost worked for me. Here is a version that does not duplicate values:
Function Get-Permutations
{
param ($array_in, $current, $depth, $array_out)
$depth++
$array_in = $array_in | select -Unique
for ($i = 0; $i -lt $array_in.Count; $i++)
{
$array_out += ($current+" "+$array_in[$i]).Trim()
if ($depth -lt $array_in.Count)
{
$array_out = Get-Permutations $array_in ($current+" "+$array_in[$i]) $depth $array_out
}
else {}
}
if(!($array_out -contains ($array_in -Join " "))) {}
for ($i = 0; $i -lt $array_out.Count; $i++)
{
$array_out[$i] = (($array_out[$i].Split(" ")) | select -Unique) -Join " "
}
$array_out | select -Unique
}