using variable in PowerShell array element - arrays

How to I add variables data inside the string element of an array? If I do $s.Length, the output is 1 instead of 2.
$IPAddress = '192.168.1.1'
[string[]]$s = (
'https://google.com/' + $IPAddress + '/hostname',
'https://google.com/' + $IPAddress + '/DNS'
)
foreach ($element in $s) {
Write-Host $element
}

The simplest way to accomplish what you are trying (string expansion) is:
$s = "https://google.com/$IPAddress/hostname",
"https://google.com/$IPAddress/DNS"
By using double quotes it will automatically expand $IPAddress within the strings. This works best when the variable is a string, as more complex objects may not perform as expected. If you need to reference a property of an object in this manner you will need to wrap it in $(), for example "Hello $($User.Name)!" to expand the Name property of the $User object.

$s contains a single string because of the way you define the array. The concatenation operator (+) has a weaker precedence than the array construction operator (,). Because of that a statement
'foo' + $v + 'bar', 'foo' + $v + 'baz'
actually works like this:
'foo' + $v + #('bar', 'foo') + $v + 'baz'
Due to the string concatenation operation the array is mangled into a space-separated string (the separator is defined in the automatic variable $OFS), resulting in this:
'foo' + $v + 'bar foo' + $v + 'baz'
To avoid this behavior you need to either put the concatenation operations in grouping expressions:
$s = ('https://google.com/' + $IPAddress + '/hostname'),
('https://google.com/' + $IPAddress + '/DNS')
or inline the variables (requires double-quoted strings):
$s = "https://google.com/${IPAddress}/hostname",
"https://google.com/${IPAddress}/DNS"
You could also use the format operator, but that requires grouping expressions as well:
$s = ('https://google.com/{0}/hostname' -f $IPAddress),
('https://google.com/{0}/DNS' -f $IPAddress)
Side note: Casting the variable to [string[]] is optional. Using the comma operator will give you an array even without an explicit cast.

TheMadTechnician beat me to it by a few seconds, but if you prefer to construct the string expressions explicitly, wrap them in parens:
$IPAddress = '192.168.1.1'
[string[]]$s = (
('https://google.com/'+$IPAddress+'/hostname'),
('https://google.com/'+$IPAddress+'/DNS'))
foreach ($element in $s)
{
Write-Host $element
}
The parens force the expressions inside to be evaluated first.

Related

How to split string variables into two array object variables using powershell?

I need to split ips and hostnames from the below the input and convert into array object using powershell
I have a input like below
$docu_results = 'existing IPs: "192.168.1.12","","192.168.1.15","192.168.1.16"
existing hostname: "node.example.com","node1.example.com","node2.example.com",""'
my expected output would be like below
$existing_ips = "192.168.1.12","","192.168.1.15","192.168.1.16"
$existing_hosts = "node.example.com","node1.example.com","node2.example.com",""
The above variables existing_ips an d existing_hosts might have empty or null value and its equivalent values in between these variables and i need to save that in a separate variables
Final output would be like below
192.168.1..12 :: node.example.com
192.168.1..15
192.168.1..15 :: node2.example.com
192.168.1..16
missing data:
value is missing for '' -> 'node1.example.com'
value is missing for '192.168.1.16' -> ''
While there may be more concise solutions, for conceptual clarity I'd use a multi-step approach based on -split and -replace:
$docu_results = 'existing IPs: "192.168.1.12","","192.168.1.15","192.168.1.16"
existing hostname: "node.example.com","node1.example.com","node2.example.com",""'
# Extract the two sub-lists from the input string.
$ipList, $hostList =
$docu_results -split 'existing IPs:|existing hostname:' -ne ''
# Split each list into an array of its elements.
$existing_ips = $iplist.Trim() -split ',' -replace '"'
$existing_hosts = $hostList.Trim() -split ',' -replace '"'
Note:
With an array as the LHS, PowerShell comparison operators act as filters (see this answer), which means that -ne '' above filters out any empty tokens from the array that the -split operation outputs; an empty(-string) token results from the fact that one of the separators is at the very start of the input string.

Split a string into an Object array

I have this string in PowerShell:
$filter = "Name;Server;Mounted;ObjectCategory;Guid;WhenChangedUTC;WhenCreatedUTC;ObjectState"
I want to split it to get an object array, I tried:
$stringArray = $filter.Split(';')
but it's a string array.
At the end I would like to call:
... | Select-Object -Property #{Name = 'Command'; Expression = { "Insert" } }, $filterObjectArray
The $filterObjectArray doesn't work when it's a string array.
But it works if $filterObjectArray = 'Name', 'Server' ...
The problem is my custom property called Command because if I only use :
... | Select-Object -Property $filterObjectArray
it works.
Thank you for your help.
Your -Property argument must be passed as a flat array, which requires use of an expression:
# Note: Target parameter -Property is positionally implied.
Select-Object (, #{Name = 'Command'; Expression={ "Insert" }} + $filterObjectArray)
Note:
Because an expression is required that is to serve as a command argument, it must be enclosed in (...), the grouping operator.
,, the array constructor operator, is used to wrap the script block ({ ... }) in a single-element array so that applying the + operator on it performs array concatenation, which means that the elements of the RHS array, $filterObjectArray, directly become elements of the resulting array, resulting in the necessary flat array of calculated properties and property names.
As for what you tried:
#{Name = 'Command'; Expression = { "Insert" } }, $filterObjectArray
This argument, parsed in argument mode, results in a nested array: the first element is the script block ({ ... }), and the second and last element is the $filterObjectArray array as a whole, which is why the names stored in it weren't recognized.
Note that in argument mode - one of PowerShell's two fundamental parsing modes - only a small subset of operators can be used, such as , and #, the splatting operator; to use others, such as +, a grouping expression ((...)) is needed, as shown (or, for using the output from entire statements as arguments, $(...) or #(...))

Powershell array interpreted as single item

I'm just learning PowerShell, and running into an issue.
I'm trying to use an array to define a collection of folder/file paths, and then use that collection in a foreach loop. When the loop is executed, it seems to be concatenating the collection into a single path instead of separately processing each item in the array. This results in a path not being found error.
Code:
$SourcePathRoot = "C:\Temp\"
#$SourcePaths = #($SourcePathRoot + "File1.dat", $SourcePathRoot + "File2.txt")
foreach ($Path in $SourcePaths) {
Write-Host $Path
Test-Path $Path }
Output ($Path and Test-Path $Path):
C:\Temp\File1.dat C:\Temp\File2.txt
False
I know it's probably something simple - what am I doing wrong?!? Thank you.
This has to do with operator precedence and type-dependent operator behavior in PowerShell.
When evaluating an expression, , takes precedence over +.
In your example, the , operator in the middle of your array expression is therefore evaluated before the + operators are.
Let's step through:
# Okay, this is the raw expression
#($SourcePathRoot + "File1.dat", $SourcePathRoot + "File2.txt")
# , takes precedence and is evaluated first, leaving us with
#("C:\Temp\" + #("File1.data", "C:\Temp\") + "File2.txt")
The behavior of + depends on the type of the left-hand side operand - which is a string - so PowerShell will attempt string concatenation of the middle array to the first string, resulting in:
#("C:\Temp\File1.data C:\Temp\" + "File2.txt")
Which is why the result is an array consisting of 1 string with the value C:\Temp\File1.dat C:\Temp\File2.txt
Solution is simple, just split the array expression into two separate statements:
$SourcePaths = #($SourcePathRoot + "File1.dat"; $SourcePathRoot + "File2.txt")
# or
$SourcePaths = #(
$SourcePathRoot + "File1.dat"
$SourcePathRoot + "File2.txt"
)

Add new set of values to ArrayList

So I have the following ArrayList stored in $var:
ip_prefix region string
0.0.0.0/24 GLOBAL Something
0.0.0.0/24 GLOBAL Something
0.0.0.0/24 GLOBAL Something
0.0.0.0/24 GLOBAL Something
I need to add a row to this however the following code returns an error:
$var.add("127.0.0.1/32", "GLOBAL", "something")
Error:
Cannot find an overload for "Add" and the argument count: "3".
At line:1 char:1
+ $awsips.add("127.0.0.1/32", "GLOBAL", "SOMETHING")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
I'm sure it's something simple I have to adjust, however Google searches had me going around in circles.
$var = New-Object System.Collections.ArrayList
$var.Add(#{"ip_prefix" = "0.0.0.0/24"; "region" = "GLOBAL"; string = "Something"})
$var.Add(#{"ip_prefix" = "127.0.0.1/32"; "region" = "GLOBAL"; string = "SOMETHING"})
$var
$var | %{ Write-Output "$($_.ip_prefix), $($_.region), $($_.string)" }
Or:
$var = #()
$var += #{"ip_prefix" = "0.0.0.0/24"; "region" = "GLOBAL"; string = "Something"}
$var += #{"ip_prefix" = "127.0.0.1/32"; "region" = "GLOBAL"; string = "SOMETHING"}
Should do the job
$obj = New-Object PSObject -Property #{
ip_prefix = "0.0.0.0/24"
region = "GLOBAL"
string = "Something"
}
$var+= $obj
Your output suggests that your array list contains custom objects with properties ip_prefix, region, and string.
You therefore need to add a single object with the desired property values to your array list.
By contrast, you attempted to add 3 indvividual elements to the array list, which is not only conceptually wrong, but also fails syntactically, given that the .Add() method only accepts a single argument (technically, there is a method for adding multiple items, .AddRange()).
In PSv3+, syntax [pscustomobject]#{...} constructs a custom object from a hashtable literal with the definition order of the entries preserved.
$null = $var.Add(
[pscustomobject] #{ ip_prefix="127.0.0.1/32"; region="GLOBAL"; string="something" }
)
Note how $null = ... is used to suppress the .Add() method's output (the index at which the item was inserted).
SQLAndOtherStuffGuy's answer is on the right track, but beware that $var += ... silently replaces the array list stored in $var with a regular PowerShell array ([System.Object[]]).

Appending a string to each item of an array

I have an array and when I try to append a string to it the array converts to a single string.
I have the following data in an array:
$Str
451 CAR,-3 ,7 ,10 ,0 ,3 , 20 ,Over: 41
452 DEN «,40.5,0,7,0,14, 21 ,  Cover: 4
And I want to append the week of the game in this instance like this:
$Str = "Week"+$Week+$Str
I get a single string:
Week16101,NYG,42.5 ,3 ,10 ,3 ,3 , 19 ,Over 43 102,PHI,- 1,14,7,0,3, 24 ,  Cover 4 103,
Of course I'd like the append to occur on each row.
Instead of a for loop you could also use the Foreach-Object cmdlet (if you prefer using the pipeline):
$str = "apple","lemon","toast"
$str = $str | ForEach-Object {"Week$_"}
Output:
Weekapple
Weeklemon
Weektoast
Another option for PowerShell v4+
$str = $str.ForEach({ "Week" + $Week + $_ })
Something like this will work for prepending/appending text to each line in an array.
Set array $str:
$str = "apple","lemon","toast"
$str
apple
lemon
toast
Prepend text now:
for ($i=0; $i -lt $Str.Count; $i++) {
$str[$i] = "yogurt" + $str[$i]
}
$str
yogurtapple
yogurtlemon
yogurttoast
This works for prepending/appending static text to each line. If you need to insert a changing variable this may require some modification. I would need to see more code in order to recommend something.
Another solution, which is fast and concise, albeit a bit obscure.
It uses the regex-based -replace operator with regex '^' which matches the position at the start of each input string and therefore effectively prepends the replacement string to each array element (analogously, you could use '$' to append):
# Sample array.
$array = 'one', 'two', 'three'
# Prepend 'Week ' to each element and create a new array.
$newArray = $array -replace '^', 'Week '
$newArray then contains 'Week one', 'Week two', 'Week three'
To show an equivalent foreach solution, which is syntactically simpler than a for solution (but, like the -replace solution above, invariably creates a new array):
[array] $newArray = foreach ($element in $array) { 'Week ' + $element }
Note: The [array] cast is needed to ensure that the result is always an array; without it, if the input array happens to contain just one element, PowerShell would assign the modified copy of that element as-is to $newArray; that is, no array would be created.
As for what you tried:
"Week"+$Week+$Str
Because the LHS of the + operation is a single string, simple string concatenation takes place, which means that the array in $str is stringified, which by default concatenates the (stringified) elements with a space character.
A simplified example:
PS> 'foo: ' + ('bar', 'baz')
foo: bar baz
Solution options:
For per-element operations on an array, you need one of the following:
A loop statement, such as foreach or for.
Michael Timmerman's answer shows a for solution, which - while syntactically more cumbersome than a foreach solution - has the advantage of updating the array in place.
A pipeline that performs per-element processing via the ForEach-Object cmdlet, as shown in Martin Brandl's answer.
An expression that uses the .ForEach() array method, as shown in Patrick Meinecke's answer.
An expression that uses an operator that accepts arrays as its LHS operand and then operates on each element, such as the -replace solution shown above.
Tradeoffs:
Speed:
An operator-based solution is fastest, followed by for / foreach, .ForEach(), and, the slowest option, ForEach-Object.
Memory use:
Only the for option with indexed access to the array elements allows in-place updating of the input array; all other methods create a new array.[1]
[1] Strictly speaking, what .ForEach() returns isn't a .NET array, but a collection of type [System.Collections.ObjectModel.Collection[psobject]], but the difference usually doesn't matter in PowerShell.

Resources