PowerShell array/index/variable - powershell

The below code is checking in a dropdownbox, which item has been selected and then is searched in several arrays and as soon found, the loop is broken through break and I receive the correct information in "`$$($array.name)".
$search = $CreateNewUserFormDropDownBoxLocation.SelectedItem.Split('-')[$($CreateNewUserFormDropDownBoxLocation.SelectedItem.Split('-').Count - 1)].Trim() + '*'
$AllLocations = (Get-Variable -Include USPennsylvaniaAve, USSixthStreet, USRodeoDrive, USOneMicrosoftWay,`
USNorthTantauAvenue, USMarketStreet, USMainStreet, USEmilyDrive,`
USCalle8, USBroadway, US18thStreetNW, UKOxfordStreet, UKDowningStreet,`
UKBondStreet, FRRuedeRivoli, FRChampsElysees, CHBahnhofstrasse,`
CA17thAvenue) | ? {$_.value -is [array]}
foreach ($Array in $AllLocations) {
if ($array.value -like $search) {
break
}
}
$test = "`$$($array.name)"
The issue is the variable $test does contain the correct array and I am interested, to work with the index (I hope I say that correctly).
For example, after the code ran and I enter $test, the result is for example $USMarketStreet, which is one of my arrays.
If I try to get the first index by typing $test[0] I do not get the expected content from the array, it gives me $.
If I enter $test[0..4] I get:
$
U
S
M
a
My intention was, to get the first key out of the array but instead of that, I am getting the first character out of the array name.
Is there a trick, how I can get to it? I also tried to play around with $array and $array.value but no success.

Related

Getting error when adding nested hashtable to array in Powershell

I have a nested hashtable with an array and I want to loop through the contents of another array and add that to the nested hashtable. I'm trying to build a Slack message block.
Here's the nested hashtable I want to add to:
$msgdata = #{
blocks = #(
#{
type = 'section'
text = #{
type = 'mrkdwn'
text = '*Services Being Used This Month*'
}
}
#{
type = 'divider'
}
)
}
$rows = [ ['azure vm', 'centralus'], ['azure sql', 'eastus'], ['azure functions', 'centralus'], ['azure monitor', 'eastus2'] ]
$serviceitems = #()
foreach ($r in $rows) {
$servicetext = "*{0}* - {1}" -f $r[1], $r[0]
$serviceitems += #{'type'='section'}
$serviceitems += #{'text'= ''}
$serviceitems.text.Add('type'='mrkdwn')
$serviceitems.text.Add('text'=$servicetext)
$serviceitems += #{'type'='divider'}
}
$msgdata.blocks += $serviceitems
The code is partially working. The hashtables #{'type'='section'} and #{'type'='divider'} get added successfully. Trying to add the nested hashtable of #{'text' = #{ 'type'='mrkdwn' 'text'=$servicetext }} fails with this error:
Line |
24 | $serviceitems.text.Add('type'='mrkdwn')
| ~
| Missing ')' in method call.
I tried looking through various Powershell posts and couldn't find one that applies to my specific situation. I'm brand new to using hashtables in Powershell.
Complementing mklement0's helpful answer, which solves the problem with your existing code, I suggest the following refactoring, using inline hashtables:
$serviceitems = foreach ($r in $rows) {
#{
type = 'section'
text = #{
type = 'mrkdwn'
text = "*{0}* - {1}" -f $r[1], $r[0]
}
}
#{
type = 'divider'
}
}
$msgdata.blocks += $serviceitems
This looks much cleaner and thus easier to maintain in my opinion.
Explanations:
$serviceitems = foreach ... captures all output (to the success stream) of the foreach loop in variable $serviceitems. PowerShell automatically creates an array from the output, which is more efficient than manually adding to an array using the += operator. Using += PowerShell has to recreate an array of the new size for each addition, because arrays are actually of fixed size. When PowerShell automatically creates an array, it uses a more efficient data structure internally.
By writing out an inline hash table, without assigning it to a variable, PowerShell implicitly outputs the data, in effect adding it to the $serviceitems array.
We output two hash tables per loop iteration, so PowerShells adds two array elements to $serviceitems per loop iteration.
Note:
This answer addresses your question as asked, specifically its syntax problems.
For a superior solution that bypasses the original problems in favor of streamlined code, see zett42's helpful answer.
$serviceitems.text.Add('type'='mrkdwn') causes a syntax error.
Generally speaking, IF $serviceitems.text referred to a hashtable (dictionary), you need either:
method syntax with distinct, ,-separated arguments:
$serviceitems.text.Add('type', 'mrkdwn')
or index syntax (which would quietly overwrite an existing entry, if present):
$serviceitems.text['type'] = 'mrkdwn'
PowerShell even lets you access hashtable (dictionary) entries with member-access syntax (dot notation):
$serviceitems.text.type = 'mrkdwn'
In your specific case, additional considerations come into play:
You're accessing a hashtable via an array, instead of directly.
The text entry you're trying to target isn't originally a nested hashtable, so you cannot call .Add() on it; instead, you must assign a new hashtable to it.
Therefore:
# Define an empty array
$serviceItems = #()
# "Extend" the array by adding a hashtable.
# Note: Except with small arrays, growing them with +=
# should be avoided, because a *new* array must be allocated
# every time.
$serviceItems += #{ text = '' }
# Refer to the hashtable via the array's last element (-1),
# and assign a nested hashtable to it.
$serviceItems[-1].text = #{ 'type' = 'mrkdwn' }
# Output the result.
$serviceItems

Create an incrementing variable from 2 variables in PowerShell

OK, First I consider myself a newbie and have much to learn about PowerShell and this is my first post ever.
I am trying to loop through some data and put it into a custom object and put them into separate arrays for later use. The issue is that I want to create a variable representing $week_data1 by using a counter $i so I can reduce the amount of code required. I do have a concatenated variable being written out: write-host '$week++ ='$week$i But I think it is being represented as a string?
How can I get $week_data$i to represent the array to insert the data?
Input data. Each week ends on Saturday.
$week1=#('2021-05-01')
$week2=#('2021-05-02', '2021-05-03', '2021-05-04', '2021-05-05', '2021-05-06', '2021-05-07', '2021-05-08')
$week3=#('2021-05-09', '2021-05-10', '2021-05-11', '2021-05-12', '2021-05-13', '2021-05-14', '2021-05-15')
$week4=#('2021-05-16', '2021-05-17', '2021-05-18', '2021-05-19', '2021-05-20', '2021-05-21', '2021-05-22')
$week5=#('2021-05-23', '2021-05-24', '2021-05-25', '2021-05-26', '2021-05-27', '2021-05-28', '2021-05-29')
$week6=#('2021-05-30', '2021-05-31')
$month =#($week1, $week2, $week3, $week4, $week5, $week6)
Create the output structures to be populated.
$week_data1=#()
$week_data2=#()
$week_data3=#()
$week_data4=#()
$week_data5=#()
$week_data6=#()
$month_data =#($week_data1, $week_data2, $week_data3, $week_data4, $week_data5, $week_data6)
Loop through the array and count the week number that is being processed.
$i = 0
foreach($week in $month)
{ $i++
$n=0
Here I can write out a Variable and it concatenates properly.
**write-host '$week++ ='$week$i**
foreach($day in $week)
{$n++
write-host '$day ='$day
Pull in data from a .csv file to populate the custom object.
foreach($line in $csv)
{
if($line -match $day)
Match the line in the CSV file that has the correct Date in it. One line in the file per date in the month.
{ #write-host '$line.Day = ' $line.Day
# custom object to be used later
$date_data = [PSCustomObject] #{
week_numb = $i
date = $line.Day
attempts = $line.Attempts
connects = $line.Connects
}
I have tried different syntax versions but it does not work here? I want to put the custom object data into the new array for the week being processed.
#write-host '$week_data[$i]='$week_data[$i]
$week_data$i += $date_data # Add data from csv file into a
#$week_data[$i] += $date_data
}
}
}
}
Issue using $week_data$i as a variable I get an error:
At line:38 char:17
$week_data$i += $date_data # Add data from csv file into a
~~
Unexpected token '$i' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
You're looking for variable indirection, i.e. the ability to refer to a variable indirectly, by a name stored in another variable or returned from an expression.
Note, however, that there are usually superior alternatives, such as using arrays or hashtables as multi-value containers - see this answer for an example.
If you do need to use variable indirection, use Get-Variable and Set-Variable:
$week_data1 = 'foo', 'bar'
$i = 1
# Same as: $week_data1
# Note that "$" must NOT be specified as part of the name.
Get-Variable "week_data$i" -ValueOnly
# Same as: $week_data1 = 'baz', 'quux'
Set-Variable "week_data$i" baz, quux
# Updating an existing value requires nesting the two calls:
# Same as: $week_data1 += 'quuz'
Set-Variable "week_data$i" ((Get-Variable "week_data$i" -ValueOnly) + 'quuz')
As an aside: "extending" an array with += is convenient, but inefficient: a new array must be created behind the scenes every time - see this answer.
Similarly, calling cmdlets to set and get variables performs poorly compared to direct assignments and variable references.
See this answer for applying the indirection technique analogously to environment variables, using Get-Content / Set-Content and the Env: drive.
As for what you tried:
$week_data$i = ... is an assignment expression, which is interpreted as directly juxtaposing two variables, $week_data and $i, which causes the syntax error you saw.
By contrast, something like Write-Output $week_data$i is a command, and while $week_data$i is also interpreted as two variable references, as a command argument it is syntactically valid, and would simply pass the (stringified) concatenation of the two variable values; in other words: $week_data$i acts as if it were double-quoted, i.e. an expandable string, and the command is therefore equivalent to Write-Output "$week_data$i"
Unrelated to the answer, but likely helpful for you, I have a function that will determine what week in a month a given date is.
Function Get-Week{
[cmdletbinding()]
param([parameter(ValueFromPipeline)][string[]]$Date)
process{
ForEach($Day in $Date){
$DTDay=[datetime]$Day
$Buffer = ([datetime]("{0}-01-{1}" -f $DTDay.Month,$DTDay.Year)).dayofweek.value__ -1
[math]::Truncate(($DTDay.Day+$Buffer)/7)+1
}
}
}
So you feed that a string that can be converted to a date like:
'5-13-2021' | Get-Week
or
Get-Week '5-13-2021'
and you get back a number indicating what week (ending on Saturday) of the month that day falls in.

Loop through multiple array in powershell

I have 2 arrays here one contains the servername and other contains the IP.
I need to loop through them and create a key value pair like below for each server
server1:ip1
server2:ip2
I have written below code, but the problem is if i debug the code using F11, it is working fine, but i don't it gives some error which is different every time.
so feeling like it is not that reliable piece to continue.
$NewDNSEntryName = $DNSEntryName.Split(",")
$DNSIPs = $DNSIP.Split(",")
if($DNSEntryName -match "," -or $DNSIP -match ",")
{
0..($NewDNSEntryName.Count - 1) | ForEach-Object {
$fullName=""
$fullName += #("$($NewDNSEntryName[$_]):$($DNSIPs[$_])")
This is the line where i am facing trouble
0..($NewDNSEntryName.Count - 1) | ForEach-Object
Please let me know why this code is behaving like this else any alternate idea is appreciated
Assuming each item in each list corresponds with each other exactly, you can use a for loop and loop through the array indexes.
$NewDNSEntryName = $DNSEntryName.Split(",")
$DNSIPs = $DNSIP.Split(",")
for ($i = 0; $i -lt $DNSIPs.count; $i++) {
"{0}:{1}" -f $NewDNSEntryName[$i],$DNSIPs[$i]
}
For the code above to work, $DNSEntryName and $DNSIP must be single strings with commas between names and IPs. If $DNSEntryName and $DNSIP are already lists or arrays, something else will need to be done.
In your attempt, technically, your logic should work given everything written above is true. However, $fullName is emptied at every single iteration, which may produce undesirable results.

Returning a Row Count for a DataRow in PowerShell

My script is populating a datarow from a stored procedure in SQL Server. I then reference specific columns in this datarow throughout the script. What I'm trying to do is add functionality that takes action X if the row count = 0, action Y if the row count = 1, and action Z if the row count > 1.
-- PowerShell script snippet
# $MyResult is populated earlier;
# GetType() returns Name=DataRow, BaseType=System.Object
# this works
ForEach ($MyRow In $MyResult) {
$MyFile = Get-Content $MyRow.FileName
# do other cool stuff
}
# this is what I'm trying to do, but doesn't work
If ($MyResult.Count -eq 0) {
# do something
}
ElseIf ($MyResult.Count -eq 1) {
# do something else
}
Else {
# do this instead
}
I can get $MyResult.Count to work if I'm using an array, but then I can't reference $MyRow.FileName directly.
This is probably pretty simple, but I'm new to PowerShell and object-oriented languages. I've tried searching this site, The Scripting Guy's blog, and Google, but I haven't been able to find anything that shows me how to do this.
Any help is much appreciated.
It has everything to do with how you populate $MyResult. If you query the database like
$MyResult = #( << code that returns results from database >> )
that is, enclosing the code that returns your dataset/datatable from the database within #( ... ), then number of rows returned will be easily checked using $MyResult.count.
Your original code should work as-is if you populate $MyResult this way.
I know this thread is old, but if someone else finds it on Google, this should work also on PS V5:
Replace $MyResult.Count with: ($MyResult | Measure-Object | select -ExpandProperty Count)
For Example:
If (($MyResult | Measure-Object | select -ExpandProperty Count) -eq 0)
I don't have experience with PS and SQL, but I'll try to provide an answer for you. If you're object $myresult is a datarow-object, it means you only got the one row. If the results are empty, then $myresult will usually be null.
If you get one or more rows, you can put them in an array and count it. However, if your $myresult are null, and you put it in an array it will still count as one, so we need to watch out for that. Try this:
If ($MyResult -eq $null) {
# do something if no rows
}
Else If (#($MyResult).Count -eq 1) {
# do something else if there are 1 rows.
# The cast to array was only in the if-test,
# so you can reach the object with $myresult.
}
Else {
# do this if there are multiple rows.
}
Looks like this question gets a lot of views, so I wanted to post how I handled this. :)
Basically, the fix for me was to change the method I was using to execute a query on SQL Server. I switched to Chad Miller's Invoke-SqlCmd2 script: TechNet: Invoke-SqlCmd2, i.e.
# ---------------
# this code works
# ---------------
# Register the function
. .\Invoke-Sqlcmd2.ps1
# make SQL Server call & store results to an array, $MyResults
[array]$MyResults = Invoke-Sqlcmd2 -Serve
rInstance "(local)" -Query "SELECT TOP 1 * FROM sys.databases;"
If ($MyResult -eq $null) {
# do something
}
ElseIf ($MyResult.Count -eq 1) {
# do something else
}
Else {
# do this instead
}

Array unrolling in PowerShell

I'm trying to understand array unrolling in Posh and the following puzzles me. In this example I return the array wrapped in another array (using a comma), which basically gives me the original array after the outer array gets unrolled on return. Why then do I get a correct result (just the array) when I indirectly wrap the result by using the array operator (#) on a variable with the result, but get an array in an array if I use # directly on the function? Does # operator behave differently for different types of its parameter?
Is there any PS help article for the # operator and the array unrolling?
function Unroll
{
return ,#(1,2,3)
}
Write-Host "No wrapper"
$noWrapper = Unroll #This is array[3]
$noWrapper | % { $_.GetType() }
Write-Host "`nWrapped separately"
$arrayWrapper = #($noWrapper) #No change, still array[3]
$arrayWrapper | % { $_.GetType() }
Write-Host "`nWrapped directly"
$directArrayWrapper = #(Unroll) #Why is this array[1][3] then?
$directArrayWrapper | % { $_.GetType() }
Write-Host "`nThe original array in elem 0"
$directArrayWrapper[0] | % { $_.GetType() }
Thanks
If you remove the comma operator from the Unroll function, it behaves at you should expect.
function Unroll
{
return #(1,2,3)
}
In this case $arrayWrapper = #($noWrapper) is the same as $directArrayWrapper = #(Unroll).
You may find more information about array unrolling with this SO question and in this article about array literals In PowerShell
Looking at this other question, it seems that
Putting the results (an array) within a grouping expression (or
subexpression e.g. $()) makes it eligible again for unrolling.
which seems to be what happen here.