To address the title's generic question up front:
When you pass an array to a function (and nothing else), $Args
receives a single argument containing the whole array, so you must use $Args[0]
to access it.
- There is a way to pass an array as individual arguments using splatting, but it requires an intermediate variable - see bottom.
To avoid confusion around such issues, formally declare your parameters.
Try the following:
$Script:ServerList = @("Server01", "Server02", "Server03")
Function EditServerList () {
# Split the arguments, which are all contained in $Args[0],
# into the command (1st token) and the remaining
# elements (as an array).
$Cmd, $Servers = $Args[0]
If ($Cmd -eq "start"){
While ($true) {
Write-host -ForegroundColor Green $Script:ServerList
$Edits = Read-Host "Enter `"add`" or `"remove`" followed by a space-delimited list of server names"
#"# Pass the array of whitespace-separated tokens to the recursive
# invocation to perform the requested edit operation.
EditServerList (-split $Edits)
}
} ElseIf ($Cmd -eq "add") {
# Append the $Servers array to the list, weeding out duplicates and
# keeping the list sorted.
$Script:ServerList = $Script:ServerList + $Servers | Sort-Object -Unique
} ElseIf ($Cmd -eq "remove") {
# Remove all specified $Servers from the list.
# Note that servers that don't exist in the list are quietly ignored.
$Script:ServerList = $Script:ServerList | Where-Object { $_ -notin $Servers }
} Else {
Write-Host -ForegroundColor Red "ERROR!"
}
}
EditServerList start
Note how a loop is used inside the "start"
branch to avoid running out of stack space, which could happen if you keep recursing.
$Cmd, $Servers = $Args[0]
destructures the array of arguments (contained in the one and only argument that was passed - see below) into the 1st token - (command string add
or remove
) and the array of the remaining arguments (server names).
Separating the arguments into command and server-name array up front simplifies the remaining code.
The $var1, $var2 = <array>
technique to split the RHS into its first element - assigned as a scalar to $var1
- and the remaining elements - assigned as an array to $var2
, is commonly called destructuring or unpacking; it is documented in Get-Help about_Assignment Operators
, albeit without giving it such a name.
-split $Edits
uses the convenient unary form of the -split
operator to break the user input into an array of whitespace-separated token and passes that array to the recursive invocation.
Note that EditServerList (-split $Edits)
passes a single argument that is an array - which is why $Args[0]
must be used to access it.
Using PowerShell's -split
operator (as opposed to .Split(' ')
) has the added advantage of ignoring leading and trailing whitespace and ignoring multiple spaces between entries.
In general, operator -split
is preferable to the [string]
type's .Split()
method - see this answer of mine.
Not how containment operator -notin
, which accepts an array as the RHS, is used in Where-Object { $_ -notin $Servers }
in order to filter out values from the server list contained in $Servers
.
As for what you tried:
EditServerList ($Edits.split(' ') |Where {$_ -NotLike "add","remove"})
(a) mistakenly attempts to remove the command name from the argument array, even though the recursive invocations require it, but (b) actually fails to do so, because the RHS of -like
doesn't support arrays. (As an aside: since you're looking for exact strings, -eq
would have been the better choice.)
Since you're passing the arguments as an array as the first and only argument, $Inputs[0]
actually refers to the entire array (command name + server names), not just to its first element (the command name).
You got away with ($Inputs[0] -eq "add")
- even though the entire array was compared - because the -eq
operator performs array filtering if its LHS is an array, returning a sub-array of matching elements. Since add
was among the elements, a 1-element sub-array was returned, which, in a Boolean context, is "truthy".
However, your attempt to weed out the command name with where {$_ -NotLike $Inputs[0]}
then failed, and add
was not removed - you'd actually have to compare to $Inputs[0][0]
(sic).
Where {$_ -NotLike ($Inputs |Where {$_ -Notlike $Inputs[0]})}
doesn't filter anything out for the following reasons:
($Inputs |Where {$_ -Notlike $Inputs[0]})
always returns an empty array, because, the RHS of -Notlike
is an array, which, as stated, doesn't work.
- Therefore, the command is the equivalent of
Where {$_ -NotLike @() }
which returns $True
for any scalar on the LHS.
Passing an array as individual arguments using splatting
Argument splatting (see Get-Help about_Splatting
) works with arrays, too:
> function foo { $Args.Count } # function that outputs the argument count.
> foo @(1, 2) # pass array
1 # single parameter, containing array
> $arr = @(1, 2); foo @arr # splatting: array elements are passed as indiv. args.
2
Note how an intermediate variable is required, and how it must be prefixed with @
rather than $
to perform the splatting.