Not able to parse multiline array in powershell - powershell

My aws cli returns multiline array with nested hash, I am not able to parse output to get appropriate values from it
>Write-Host $a
[
[
[
{
"VolumeID": "vol-fxxxxxx"
}
]
]
]
If I do $a[0], it returns first line i.e. "[" same with incremental indexs.
How I can parse this array and get the volumeID from it ?
Thanks
Additional details :
Thanks for your valuable answers. Here is the class name of the above object :
> $a.getType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
> $a | Where-Object ($_ -like "*Volume*")
> $a | ConvertFrom-Json
ConvertFrom-Json : Invalid JSON primitive:
Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData.
At line:1 char:6
+ $a | ConvertFrom-Json
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [ConvertFrom-Json], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException,Microsoft.PowerShell.Commands.ConvertFromJsonCommand
Hi Martin Brandl, TessellatingHeckler I have updated with your question, could you please help me out on the same.
Thanks

same with incremental indexs
I can't make sense of incremental indexes doing the same, and returning the first line, but it looks like it's either:
A multiline string ($a.gettype() has Name: String):
$b = $a | ConvertFrom-Json
$b[0][0][0].VolumeID
or, an array of string ($a.gettype() Name is Object[] or String[]), do this first, then convert from JSON:
$a = -join $a

You could use the where-object cmdlet.
$a | where-object {$_ -like "*Volume*"}
More information on the where-object cmdlet can be found here: https://technet.microsoft.com/en-us/library/ee177028.aspx
Thanks, Tim.

Related

Trouble accessing members of select-string output when input is a system.array

I am calling restAPIs and some of them return multiple results, I am trying use Select-String to get the correct line in the Array, but it returns a matchinfo object with a value starting with #{... I can't get this value into a hashtable or object so I can extract a member from the string.
I tried converting the MatchInfo object to a string with out-string and then putting that result in a hashtable. Get following error:
Cannot convert the "
#{id=352475; href=/api/v1/exports/458234/export_files/352475; export_id=458234; status=Available}
" value of type "System.String" to type "System.Collections.Hashtable".
Have a PSCustomObject with the following contents:
PS C:> $_expFilesRet
href export_files
---- ------------
/api/v1/user_identities/289362/export_files {#{id=352475;
href=/api/v1/exports/458234/export_files/3...
The export files method of the above is
PS C:\> $_ExpFilesRet.export_files.getType();
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
with the following value:
PS C:\> $_expFilesRet.export_files
id href export_id status
-- ---- --------- ------
352475 /api/v1/exports/458234/exp... 458234 Available
278697 /api/v1/exports/357459/exp... 357459 Available
Attempted the following
PS C:\> $_temp=$_ExpFilesRet.export_files | select-string -pattern $_postret.export_files.export_id
PS C:\> $_temp
#{id=352475; href=/api/v1/exports/458234/export_files/352475; export_id=458234; status=Available}
PS C:\> $_temp=$_ExpFilesRet.export_files | select-string -pattern $_postret.export_files.export_id | out-str
ing -width 1000
PS C:\> $_temp
#{id=352475; href=/api/v1/exports/458234/export_files/352475; export_id=458234; status=Available}
PS C:\> [hashtable]$_temp=$_temp
Cannot convert the "
#{id=352475; href=/api/v1/exports/458234/export_files/352475; export_id=458234; status=Available}
" value of type "System.String" to type "System.Collections.Hashtable".
At line:1 char:1
+ [hashtable]$_temp=$_temp
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException
+ FullyQualifiedErrorId : RuntimeException
I am trying to get the value of ID (352475) from the resulting string in $_temp.
Use Where-Object rather than Select-String to filter objects:
$_temp = $_ExpFilesRet.export_files |Where-Object export_id -eq $_postret.export_files.export_id |Select -Expand id

getting property value in powershell when running convertto-html

I'm still new to powershell, and recently i came to know about get-member properties. However when i pipe the command to convertto-html the value disappear.
i have a data which is called link.csv and below is the content of the file
Target Node Address Status
------ ---- ------- ------
server01 0:2:3 20230002AC0153AF Up
server01 0:2:4 20240002AC0153AF Up
server01 1:2:3 21230002AC0153AF Up
server01 1:2:4 21240002AC0153AF Up
I'm able to get the property value as per below.
PS C:\Report\script\Temp> $a= Import-Csv .\link.csv | select Target,Node,Address,Status
PS C:\Report\script\Temp> $a.Node[1]
0:2:4
PS C:\Report\script\Temp> $a.Status[1]
Up
PS C:\Report\script\Temp> $a.Target[1]
server01
However when i output it to Convertto-Html
PS C:\Report\script\Temp> $a= Import-Csv .\link.csv | select Target,Node,Address,Status | ConvertTo-Html -Fragment -PreContent "<font color=`"Black`"><H4>Remote Copy Group - $b</H4></font> "
PS C:\Report\script\Temp> $a.Target[1]
Cannot index into a null array.
At line:1 char:1
+ $a.Target[1]
+ ~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
How can i get the property value while running convertto-html command.
Hope you can help me with the issue.
Thank You in advance.
If you want to capture intermediate results in the pipeline, use the -OutVariable / -ov common parameter with a variable name in order to store these results in that variable:
$html = Import-Csv .\link.csv |
Select-Object Target, Node, Address, Status -OutVariable a |
ConvertTo-Html -Fragment -PreContent "<font color=`"Black`"><H4>Remote Copy Group - $b</H4></font> "
$a.Target[1]
Note how the pipeline overall returns HTML (as an array of lines), whereas the intermediate custom objects filtered from the CSV input are stored in variable $a via -OutVariable, for later use - note how you must pass just the variable name to -OutVariable, without the $ sigil, i.e., just a rather than $a.

Instantiate empty csv variable

I'm using methods to pick records from different csv files and I want to know how to create an empty variable that would act like a csv file. For Example...
an empty array would be $array = #()
an empty hash table would be $hashTable = #{}
a non-empty csv object would be $csvFileRecords = Import-Csv $someFileName
an empty csv object would be ????
What would the syntax be?
A "CSV object" is actually just an array (of psobjects).
$csvFileRecords = #()
To add new records to that array, you just need to add psobjects with appropriate attributes corresponding to the desired columns. One way to create a psobject with desired properties is from a hash table.
$obj = new-object psobject -property #{fname="Fiddle";lname="Freak"}
$csvFileRecords += $obj
Well, CSV's can't created from $null, but you could do something like this:
PS C:\> New-Object -TypeName System.Object | ConvertTo-Csv
#TYPE System.Object
But your challenge is that this doesn't work back and forth. For example:
PS U:\> ((New-Object -TypeName System.Int32 ) | ConvertTo-Csv).GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
means that the output of ConvertTo-CSV is not null, but this shows where you've lost the pipeline:
PS U:\> ((New-Object -TypeName System.Int32 ) | ConvertTo-Csv | ConvertFrom-csv).GetType()
You cannot call a method on a null-valued expression.
At line:1 char:1
+ ((New-Object -TypeName System.Int32 ) | ConvertTo-Csv | ConvertFrom-c ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
The help says:
Inputs
System.Management.Automation.PSObject
You can pipe any object that has an Extended Type System (ETS) adapter to ConvertTo-CSV.
So I bet your issue is that $null or blank doesn't have an ETS Adapter.
Here's some more reading on this: https://blogs.msdn.microsoft.com/besidethepoint/2011/11/22/psobject-and-the-adapted-and-extended-type-systems-ats-and-ets/

Array.Find on powershell array

How can I use the Array.Find method in powershell?
For example:
$a = 1,2,3,4,5
[Array]::Find($a, { args[0] -eq 3 })
gives
Cannot find an overload for "Find" and the argument count: "2".
At line:3 char:1
+ [Array]::Find($a, { $args[0] -eq 3 })
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
The array class has the methods I expect, as given by:
PS> [Array] | Get-Member -Static
TypeName: System.Array
Name MemberType Definition
---- ---------- ----------
Find Method static T Find[T](T[] array, System.Predicate[T] match)
Should the array be casted to something else to match the T[] type? I know there are other ways to achieve the find functionality, but I was wondering why this doesn't work.
There is no need to use Array.Find, a regular where clause would work fine:
$a = #(1,2,3,4,5)
$a | where { $_ -eq 3 }
Or this (as suggested by #mjolinor):
$a -eq 3
Or this (returns $true or $false):
$a -contains 3
Where clause supports any type of objects, not just basic types, like this:
$a | where { $_.SomeProperty -eq 3 }
You need to cast the ScriptBlock as a Predicate[T]. Consider the following example:
[Array]::Find(#(1,2,3), [Predicate[int]]{ $args[0] -eq 1 })
# Result: 1
The reason that you received the error, is because there was no matching method overload, in the case where you're passing in a PowerShell ScriptBlock. As you noted in your Get-Member output, there is no Find() method overload that accepts a ScriptBlock as its second parameter.
[Array]::Find(#(1,2,3), { $args[0] -eq 1 })
Cannot find an overload for "Find" and the argument count: "2".
At line:1 char:17
+ [Array]::Find(#(1,2,3), { $_ -eq 1 })
+ ~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
Another option would be using an ArrayList, which provides a Contains method:
PS C:\> [Collections.ArrayList]$a = 'a', 'b', 'c'
PS C:\> $a.Contains('b')
True
PS C:\> $a.Contains('d')
False
Or, as #Neolisk mentioned in the comments, you could use PowerShell's -contains operator:
PS C:\> $a = 'a', 'b', 'c'
PS C:\> $a -contains 'b'
True
PS C:\> $a -contains 'd'
False
Trevor Sullivan's answer is the right one, not only for Find() static method, but for FindIndex() as well.
When you've got several NIC cards with both ipv4 & ipv6 active on your servers and want to check the ipv4 IP/netmask pairs, something like this is good :
$NetworkAdapters = Get-WmiObject Win32_NetworkAdapterConfiguration -Filter 'IPEnabled = True' | Select-Object -Property Description, IPAddress, IPSubnet, DefaultIPGateway, DNSServerSearchOrder, DNSDomain
$NetworkAdapters | % {
"Adapter {0} :" -f $_.Description
# array'ing to avoid failure against single homed netcards
$idx = [System.Array]::FindIndex(#($_.IPAddress), [Predicate[string]]{ $args[0] -match "\d+.\d+.\d+.\d+" })
" IP {0} has netmask {1}" -f #($_.IPAddress[$idx]), #($_.IPSubnet)[$idx]
}
My point is it works like a charm on 2012 WinPE, and fails on a production Win7 wks. Anyone got an idea ?
This was run across ~6 million items in a system.array using both methods
$s=get-date
$([array]::FindALL($OPTArray,[Predicate[string]]{ $args[0] -match '^004400702(_\d{5})?' })).count
$(New-TimeSpan -Start $s -End $(get-date)).TotalSeconds
20 items
33.2223219 seconds
$s=get-date
$($OPTArray | where { $_ -match '^004400702(_\d{5})?'}).count
$(New-TimeSpan -Start $s -End $(get-date)).TotalSeconds
20 items
102.1832173 seconds

Why don't errors get returned when calling properties that don't exist?

Given the following snippet
$drives = Get-PSDrive
foreach($drive in $drives)
{
Write-Host $drive.Name "`t" $drive.Root
Write-Host " - " $drive.Free "`t" $drive.PropertyDoesntExist
}
The drive.PropertyDoesntExist property doesn't... erm... exist so I would expect an error to be thrown but instead it returns a null.
How can I get errors or exceptions?
EDIT - Me bad - I asked 2 questions in one so I moved one into a separate question.
The NextHop Blog provides a good solution to this problem. It doesn't give you an error, but instead a boolean. You can use Get-Member to get a collection of all of the real properties of the object's type and then match for your desired property.
Here's an example for strings:
PS C:\> $test = "I'm a string."
PS C:\> ($test | Get-Member | Select-Object -ExpandProperty Name) -contains "Trim"
True
PS C:\> ($test | Get-Member | Select-Object -ExpandProperty Name) -contains "Pigs"
False
If you explicitly want an error, you may want to look into Set-Strictmode as Set-StrictMode -version 2 to trap non-existent properties. You can easily turn it off when you're done with it, too:
PS C:\> Set-StrictMode -version 2
PS C:\> "test".Pigs
Property 'Pigs' cannot be found on this object. Make sure that it exists.
At line:1 char:8
+ "test". <<<< Pigs
+ CategoryInfo : InvalidOperation: (.:OperatorToken) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFoundStrict
PS C:\> Set-StrictMode -off