Powershell Select-Object Property multiple comparisons same property - powershell

I am using Select-Object to filter a CSV to get the necessary columns.
When I use this: $Filter = $Csv | Select-Object ($Csv[0].PSObject.Properties.Name -like "*Results*" it filters all columns and displays everything containing results, this works fine. But how do I get it to keep my first column where the header is "Sample" as well as keeping the results? I have tried without success:
$Filter = $Csv | Select-Object ($Csv[0].PSObject.Properties.Name -like "*Results*" -and $Csv[0].PSObject.Properties.Name -like "Sample")
I understand you can add multiple properties comma separated but I am looking for the same property but with multiple matching parameters.
The output would include a column that have header name "Sample" and columns that would contain the word "Results". They both work individually in the first line of code provided, but how do i make it work together with both matching strings?
Edit: Expected output added

Select-Object's -Property parameter takes an array of property names (see documentation at Select-Object).
Your individual expressions ($Csv[0].PSObject.Properties.Name -like "*Results*" and $Csv[0].PSObject.Properties.Name -like "Sample") each individually return an array of matching columns as expected, but when you -and them together it becomes a boolean expression that returns $true or $false so your Select-Object becomes the equivalent of:
$Filter = $Csv | Select-Object -Property #( $true )
To demonstrate this, we'll use some sample data:
$csv = #"
Sample, Sample2, Something Else, A1Results, ResultsZ2
aaa, bbb, ccc, ddd, eee
"#
$data = $csv | ConvertFrom-Csv
and then see what your individual expressions return:
PS> ($data[0].PSObject.Properties.Name -like "*Results*")
A1Results
ResultsZ2
PS> ($data[0].PSObject.Properties.Name -like "Sample")
Sample
and now we'll try your combined expression:
PS> ($data[0].PSObject.Properties.Name -like "*Results*") -and ($data[0].PSObject.Properties.Name -like "Sample")
True
If you want to generate the list of all matching columns you'll need to combine the two separate lists in a different way - you can add them together and PowerShell will return a new array that contains the two separate arrays concatenated:
PS> ($data[0].PSObject.Properties.Name -like "*Results*") + ($data[0].PSObject.Properties.Name -like "Sample")
A1Results
ResultsZ2
Sample
and then if you plug that back into your original code you get this:
PS> $data | Select-Object ( ($data[0].PSObject.Properties.Name -like "*Results*") + ($data[0].PSObject.Properties.Name -like "Sample") )
A1Results ResultsZ2 Sample
--------- --------- ------
ddd eee aaa
Cheeky Update
#mklement0 notes in their answer that the left-hand side of the + needs to be an array in order for the addition operator to trigger array concatenation. You can ensure this by coercing the result of the individual expressions into arrays using the Array Subexpression Operator like this:
PS> $data | Select-Object ( #($data[0].PSObject.Properties.Name -like "*Results*") + #($data[0].PSObject.Properties.Name -like "Sample") )

In order to pass multiple property (column) names to the (possibly positionally implied) -Property parameter of the Select-Object cmdlet using an expression ((...)), you must pass a flat array.
To that end, use + for concatenation, making sure that (at least) the LHS is an array.
The following places the Sample property first, using #(...), the array-subexpression operator to wrap it in an array:
$Csv |
Select-Object (#('Sample') + ($Csv[0].PSObject.Properties.Name -like "*Results*"))

Related

Powershell get-DhcpServerv4Scope Export-csv trouble [duplicate]

I am new to PowerShell and trying to get a list of VM names and their associated IP Addresses from within Hyper-V.
I am getting the information fine on the screen but when I try to export to csv all I get for the IP Addresses is System.Collections.Generic.List`1[System.String] on each line.
There are suggestions about "joins" or "ConvertTo-CSV" but I don't understand the syntax for these.
Can anyone help?
This is the syntax I am using...
Get-VM | Select -ExpandProperty VirtualNetworkAdapters | select name, IPV4Addresses | Export-Csv -Path "c:\Temp\VMIPs.csv"
If an object you export as CSV with Export-Csv or ConvertTo-Csv has property values that contain a collection (array) of values, these values are stringified via their .ToString() method, which results in an unhelpful representation, as in the case of your array-valued .IPV4Addresses property.
To demonstrate this with the ConvertTo-Csv cmdlet (which works analogously to Export-Csv, but returns the CSV data instead of saving it to a file):
PS> [pscustomobject] #{ col1 = 1; col2 = 2, 3 } | ConvertTo-Csv
"col1","col2"
"1","System.Object[]"
That is, the array 2, 3 stored in the .col2 property was unhelpfully stringified as System.Object[], which is what you get when you call .ToString() on a regular PowerShell array; other .NET collection types - such as [System.Collections.Generic.List[string]] in your case - stringify analogously; that is, by their type name.
Assuming you want to represent all values of an array-valued property in a single CSV column, to fix this problem you must decide on a meaningful string representation for the collection as a whole and implement it using Select-Object with a calculated property:
E.g., you can use the -join operator to create a space-separated list of the elements:
PS> [pscustomobject] #{ col1 = 1; col2 = 2, 3 } |
Select-Object col1, #{ n='col2'; e={ $_.col2 -join ' ' } } |
ConvertTo-Csv
"col1","col2"
"1","2 3"
Note how array 2, 3 was turned into string '2 3'.
OtherObjectPipedStuff | Select-object name,IPV4Addresses | export-csv PP.csv -NoTypeinformation

Compare 2 arrays with powershell

I have 2 Arrays $UsersGroup and $UsersActive, i need to find where in $UsersActive i have a line with SamAccountName and the ObjectGUID .
$UsersGroup =
SamAccountName ObjectGUID
-------------- ----------
XXXX00XX 0031e949-9120-4df1-bddb-98067a141448
XXXX01XX 0031e949-9120-4df1-bdgb-99067a141448
XXXX02XX 0031e949-9120-4df1-bdab-97067a141448
and without headers
$UsersActive =
fcb483fa146b
fcb515739a2f
fcb82f1ef74c
fcc5ee8b8722
fcd3f1f471c2
fceb26a598a3
fd0b14cecd0e
98067a141448
I need to have the match user from $UsersActive to $UserGroup.Object like that
$UsersGroup | ForEach-Object {if($_.ObjectGUID -contains $UsersActive) {$_}}
But i don't get the result like that :
XXXX00XX 0031e949-9120-4df1-bddb-98067a141448
Can some one help me , thanks !
-contains is a collection containment operator, testing for the exact occurrence of the right-hand side argument in the left-hand side argument.
To test for the presence of a substring in a string, use the -like wildcard string comparison operator:
$UsersGroup | Where-Object {
$guid = $_.ObjectGUID
$UsersActive.Where({$guid -like "*$_*"}, 'First')
}
Each group entry will now be tested against every $UsersActive value until a match is found (causing Where-Object to pass the object through) or no match is found (causing Where-Object to filter out the object)
If I understand you correctly, using compare-object... This has only one match. Compare-object can be slow for large lists.
$usersgroup =
'SamAccountName,ObjectGUID
XXXX00XX,0031e949-9120-4df1-bddb-98067a141448
XXXX01XX,0031e949-9120-4df1-bdgb-99067a141448
XXXX02XX,0031e949-9120-4df1-bdab-97067a141448' | convertfrom-csv
$usersactive = -split
'fcb483fa146b
fcb515739a2f
fcb82f1ef74c
fcc5ee8b8722
fcd3f1f471c2
fceb26a598a3
fd0b14cecd0e
98067a141448'
compare ($usersgroup.objectguid -replace '.*-') $usersactive -IncludeEqual |
? sideindicator -eq '==' # order doesn't matter
InputObject SideIndicator
----------- -------------
98067a141448 ==
To offer an alternative solution:
As Mathias states in his answer, the -contains operator and its operands-reversed counterpart, -in, only perform equality comparison of a single comparison value against the elements of a collection (array).
As an aside: Given that $_.ObjectGUID is the scalar (single object) and $UsersActive the collection (array) to search in, you would have needed to use -in, not -contains ($_.ObjectGUID -in $UsersActive)
Unfortunately, as of version 7.3.2, PowerShell's pattern-matching operators, -like (with wildcard expressions) and -match (with regexes), do not support matching against multiple (an array of) patterns.
However, since you're looking to match a literal substring of each .ObjectGUID value, you can use -in if you extract that relevant substring first and use it as the comparison value:
# -> Object whose GUID is '0031e949-9120-4df1-bddb-98067a141448'
$usersGroup | Where-Object { ($_.ObjectGUID -split '-')[-1] -in $usersActive }
Note how the -split operator is used to split the GUID into tokens by -, with [-1] returning the last token, which -in then looks for by equality comparison in the $UsersActive array.
As an aside:
Allowing multiple patterns as the RHS of -like and -match - so as to return $true if any of them match - would be a helpful future improvement.
GitHub issue #2132 asks for just that.

Format MemberOf and Members [duplicate]

I am new to PowerShell and trying to get a list of VM names and their associated IP Addresses from within Hyper-V.
I am getting the information fine on the screen but when I try to export to csv all I get for the IP Addresses is System.Collections.Generic.List`1[System.String] on each line.
There are suggestions about "joins" or "ConvertTo-CSV" but I don't understand the syntax for these.
Can anyone help?
This is the syntax I am using...
Get-VM | Select -ExpandProperty VirtualNetworkAdapters | select name, IPV4Addresses | Export-Csv -Path "c:\Temp\VMIPs.csv"
If an object you export as CSV with Export-Csv or ConvertTo-Csv has property values that contain a collection (array) of values, these values are stringified via their .ToString() method, which results in an unhelpful representation, as in the case of your array-valued .IPV4Addresses property.
To demonstrate this with the ConvertTo-Csv cmdlet (which works analogously to Export-Csv, but returns the CSV data instead of saving it to a file):
PS> [pscustomobject] #{ col1 = 1; col2 = 2, 3 } | ConvertTo-Csv
"col1","col2"
"1","System.Object[]"
That is, the array 2, 3 stored in the .col2 property was unhelpfully stringified as System.Object[], which is what you get when you call .ToString() on a regular PowerShell array; other .NET collection types - such as [System.Collections.Generic.List[string]] in your case - stringify analogously; that is, by their type name.
Assuming you want to represent all values of an array-valued property in a single CSV column, to fix this problem you must decide on a meaningful string representation for the collection as a whole and implement it using Select-Object with a calculated property:
E.g., you can use the -join operator to create a space-separated list of the elements:
PS> [pscustomobject] #{ col1 = 1; col2 = 2, 3 } |
Select-Object col1, #{ n='col2'; e={ $_.col2 -join ' ' } } |
ConvertTo-Csv
"col1","col2"
"1","2 3"
Note how array 2, 3 was turned into string '2 3'.
OtherObjectPipedStuff | Select-object name,IPV4Addresses | export-csv PP.csv -NoTypeinformation

Count unique numbers in CSV (PowerShell or Notepad++)

How to find the count of unique numbers in a CSV file? When I use the following command in PowerShell ISE
1,2,3,4,2 | Sort-Object | Get-Unique
I can get the unique numbers but I'm not able to get this to work with CSV files. If for example I use
$A = Import-Csv C:\test.csv | Sort-Object | Get-Unique
$A.Count
it returns 0. I would like to count unique numbers for all the files in a given folder.
My data looks similar to this:
Col1,Col2,Col3,Col4
5,,7,4
0,,9,
3,,5,4
And the result should be 6 unique values (preferably written inside the same CSV file).
Or would it be easier to do it with Notepad++? So far I have found examples only on how to count the unique rows.
You can try the following (PSv3+):
PS> (Import-CSV C:\test.csv |
ForEach-Object { $_.psobject.properties.value -ne '' } |
Sort-Object -Unique).Count
6
The key is to extract all property (column) values from each input object (CSV row), which is what $_.psobject.properties.value does;
-ne '' filters out empty values.
Note that, given that Sort-Object has a -Unique switch, you don't need Get-Unique (you need Get-Unique only if your input already is sorted).
That said, if your CSV file is structured as simply as yours, you can speed up processing by reading it as a text file (PSv2+):
PS> (Get-Content C:\test.csv | Select-Object -Skip 1 |
ForEach-Object { $_ -split ',' -ne '' } |
Sort-Object -Unique).Count
6
Get-Content reads the CSV file as a line of strings.
Select-Object -Skip 1 skips the header line.
$_ -split ',' -ne '' splits each line into values by commas and weeds out empty values.
As for what you tried:
Import-CSV C:\test.csv | Sort-Object | Get-Unique:
Fundamentally, Sort-Object emits the input objects as a whole (just in sorted order), it doesn't extract property values, yet that is what you need.
Because no -Property argument is passed to Sort-Object to base the sorting on, it compares the custom objects that Import-Csv emits as a whole, by their .ToString() values, which happen to be empty[1]
, so they all compare the same, and in effect no sorting happens.
Similarly, Get-Unique also determines uniqueness by .ToString() here, so that, again, all objects are considered the same and only the very first one is output.
[1] This may be surprising, given that using a custom object in an expandable string does yield a value: compare $obj = [pscustomobject] #{ foo ='bar' }; $obj.ToString(); '---'; "$obj". This inconsistency is discussed in this GitHub issue.

Powershell using where-object

I am using Powershell and am having trouble with the Where-Object cmdlet. I currently select * and then want to only output when a field is equal to Alabama. This field could be under any column, not just one.
This is what I have:
select * | where {$_.state_name -eq 'Alabama'} .
This works for state_name, but i cant get all columns without doing them individually. I've tried where{$_ -eq....} but that doesn't work.
Kind of a hack, but:
select * | where {($_ | ConvertTo-Csv -NoTypeInformation)[1] -like '*"Alabama"*'}
You have to iterate over all object's properties and check if it contains word 'Alabama'.
Example:
# Import CSV file and feed it to the pipeline
Import-Csv -Path .\My.csv |
# For each object
ForEach-Object {
# Psobject.Properties returns all object properties
# (Psobject.Properties).Value returns only properties' values
# -contains operator checks if array of values contains exact string 'Alabama'
# You can also use -like operator with wildcards, i.e. -like '*labama'
if(($_.PSObject.Properties).Value -contains 'Alabama')
{
# If any of the object properties contain word 'Alabama',
# write it to the pipeline, else do nothing.
$_
}
}