Powershell Where command not working - powershell

I have a text file that contains elements separated by an '=' sign (i.e. color1=red, color2=blue, etc.
I used the import-csv command and provide headers (i.e.
$Import_Cfg = Import-Csv .\Env.cfg -Header Title,Setting -Delimiter =
)
Now this works fine if I want to assign a particular item to another variable if I know the index number and I have used that approach but it won't always work for me because I don't always know what other data will be there.
I thought that by using something like:
$MyColor1 = $Import_Cfg.Setting |where {$_.Title -match "Blue"}
$MyColor2 = $Import_Cfg.Setting |where {$_.Title -match "Red"}
it should work, but I get no returns for either item. When I type in $Import_cfg I can see the entire array (without the "=" signs). If I tell use the command
$MyColor1 = $import_cfg[0].setting
I get the right answer.
Obviously I'm not using colors but a bunch of different items that I need to assign to variables for use elsewhere. Any thoughts on what I'm doing wrong? Everything I've read says that what I have above should work.
Please no flames on why I'm using import-csv vs get-content. I'm sure either will work. This is an approach that I've used and computationally it doesn't matter. If programatically it makes a difference I'm all ears!!!
Thanks for all your help.

The value of the Setting property itself has no Title property.
You need to apply Where before you extract the property value you need (as mentioned in the comments):
$BlueSettings = $Import_Cfg |where {$_.Title -match "Blue"} |Select-Object -ExpandProperty Setting
or, using property enumeration:
$BlueSettings = ($Import_Cfg |where {$_.Title -match "Blue"}).Setting

Related

Issues getting get-adcomputer to recognize variable in filter

Below is the code I am working with. I have verified that the initial import-csv is working as it should be, and if I change out the variable object for a concrete object, the script works as it should. It just seems to not recognize/use the variable the way that it should.
$CSVOutput = "C:\temp\output.csv"
$Output = foreach($u in $userlastname)
{
Get-ADComputer -Filter {Description -Like '*$u*'} -properties Description | Select Name, Description
}
$Output | Export-Csv $CSVOutput
If I replace the $u in the filter with one of the values from the $userlastname variable, it works, but it just runs the search with the set value as many times as it runs the foreach loop. I am expecting to see several different computer objects that have the different values from $userlastname in their description. Currently it returns nothing, as if it found no values that matched in the description field.
While it’s technically possible to use a scriptblock as a filter in the ADCommands, it isn’t recommended - use a string instead:
Get-ADComputer -Filter "Description -like '*$($u.name)*'" -Properties ...
Using a string will solve your variable substitution issue.
ETA: Comments indicated that you were getting #{Name=User} as the expansion for $u in the filter expression. This is because $u was a structured [PSCustomObject], and you were looking for a single field from that object. The easiest way to get the value of the desired field of the object is simply to use the PowerShell evaluation construct, as given in the edited answer.

Using Where-Object to find files created within time range

all. I'm trying to take the creation date of a file in one folder and use it to filter files in another while also making sure they contain the phrase 'MS'. This is what I have so far:
$MSdat_time = $MSdat_file.CreationTime
# Defining maximum and minimum auto save creation times with a window of +/- 5 min
$auto_maxtime = ($MSdat_time).AddMinutes(5)
$auto_mintime = ($MSdat_time).AddMinutes(-5)
# Locating any Auto Save files created within time frame using 'MS' pattern as a parameter in case of multiple files
$autsav_file = Get-ChildItem "\\IP.Address\Test Data\Auto Saves" | `
Where-Object {($_.LastWriteTime -ge $auto_mintime) -and ($_.LastWriteTime -le $auto_maxtime)} | `
Select-String -Pattern 'MS' | Select-Object -Unique Path
I put in 'IP address' as a place holder. So far, it's returning nothing even though I know a file with those parameters exists and this section of code was working fine yesterday.
Check that your $MSdat_file variable has a value. It could be that your previous PS sessions had given that variable a value outside of your script.
After assigning $MSdat_file = Get-Item "./out.wav", I was able to get expected output:
PS /Users/ethansmith> /Users/ethansmith/Documents/test.ps1
PS /Users/ethansmith> $autsav_file
Path
----
/Users/ethansmith/Documents/test.ps1
Thank you everyone for your help! #ethan was right, the issue was with my $MSdat_file variable having an old value saved to it. Should have traced my code back to the beginning sooner haha. Thanks everyone!

PowerShell - Why is an array used in this line of code, please?

I have the following code that looks for a string in multiple files, part of which I found here.
$path = C:\Windows
Set-Location -path $path
$searchWords = 'log.txt'
Foreach ($sw in $searchWords)
{
Get-Childitem -Path $path -Recurse -include "*.txt","*.dll" |
Select-String -Pattern "$sw" |
Select Path,LineNumber,#{n='SearchWord';e={$sw}}
}
The syntax I don't think I understand is this part in the last line:
#{n='SearchWord';e={$sw}}
I'll explain what I think I understand and then ask questions.
# I think means it is an array
n= is shorthand for 'name'
the colon(;) is separating the name of the column and the expression that fills the column.
e= is shorthand for expression
{$sw} - the brackets are necessary here to encapsulate the expression.
Question(s):
Why is an array used to populate this column?
Why must an expression be used and not just the variable '$sw'?
Thanks for the help!
It's not an array but hash table. In the quoted code, a calculated property is used. Usually calculated properties are used to, well, calculate stuff. For example, free disk space can be calculated as percents as per this answer:
#{Name = 'Free';Expression = { "{0:N0}%" -f (($_.FreeSpace/$_.Size) * 100) } }
In the sample you used, calculated property is used to add a label property that contains the search term used on each iteration of the foreach loop.
Thanks to everyone. I didn't know what a calculated property was. #vonPryz helped me understand this. For others trying to understand calculated properties, here are some links which further explain Calculated Properties.
Adam Bertram: Using PowerShell's Calculated Properties
Microsoft Documentation: Calculated Properties

Reformat column names in a csv with PowerShell

Question
How do I reformat an unknown CSV column name according to a formula or subroutine (e.g. rename column " Arbitrary Column Name " to "Arbitrary Column Name" by running a trim or regex or something) while maintaining data?
Goal
I'm trying to more or less sanitize columns (the names) in a hand-produced (or at least hand-edited) csv file that needs to be processed by an existing PowerShell script. In this specific case, the columns have spaces that would be removed by a call to [String]::Trim(), or which could be ignored with an appropriate regex, but I can't figure a way to call or use those techniques when importing or processing a CSV.
Short Background
Most files and columns have historically been entered into the CSV properly, but recently a few columns were being dropped during processing; I determined it was because the files contained a space (e.g., Select-Object was being told to get "RFC", but Import-CSV retrieved "RFC ", so no matchy-matchy). Telling the customer to enter it correctly by hand (though preferred and much simpler) is not an option in this case.
Options considered
I could manually process the text of the file, but that is a messy and error prone way to re-invent the wheel. I wonder if there's a syntax with Select-Object that would allow a softer match for column names, but I can't find that info.
The closest I have come conceptually is using a calculated property in the call to Select-Object to rename the column, but I can only find ways to rename a known column to another known column. So, this would require enumerating the columns and matching them exactly (preferred) or a softer match (like comparing after trimming or matching via regex as a fallback) with expected column names, then creating a collection of name mappings to use in constructing calculated properties from that information to select into a new object.
That seems like it would work, but more it's work than I'd prefer, and I can't help but hope that there's a simpler way I haven't been able to find via Google. Maybe I should try Bing?
Sample File
Let's say you have a file.csv like this:
" RFC "
"1"
"2"
"3"
Code
Now try to run the following:
$CSV = Get-Content file.csv -First 2 | ConvertFrom-Csv
$FixedHeaders = $CSV.PSObject.Properties.Name.Trim(' ')
Import-Csv file.csv -Header $FixedHeaders |
Select-Object -Skip 1 -Property RFC
Output
You will get this output:
RFC
---
1
2
3
Explanation
First we use Get-Content with parameter -First 2 to get the first two lines. Piping to ConvertFrom-Csv will allow us to access the headers with PSObject.Properties.Name. Use Import-Csv with the -Header parameter to use the trimmed headers. Pipe to Select-Object and use -Skip 1 to skip the original headers.
I'm not sure about comparisons in terms of efficiency, but I think this is a little more hardened, and imports the CSV only once. You might be able to use #lahell's approach and Get-Content -raw, but this was done and it works, so I'm gonna leave it to the community to determine which is better...
#import the CSV
$rawCSV = Import-Csv $Path
#get actual header names and map to their reformatted versions
$CSVColumns = #{}
$rawCSV |
Get-Member |
Where-Object {$_.MemberType -eq "NoteProperty"} |
Select-Object -ExpandProperty Name |
Foreach-Object {
#add a mapping to the original from a trimmed and whitespace-reduced version of the original
$CSVColumns.Add(($_.Trim() -replace '(\s)\s+', '$1'), "$_")
}
#Create the array of names and calculated properties to pass to Select-Object
$SelectColumns = #()
$CSVColumns.GetEnumerator() |
Foreach-Object {
$SelectColumns += {
if ($CSVColumns.values -contains $_.key) {$_.key}
else { #{Name = $_.key; Expression = $CSVColumns[$_.key]} }
}
}
$FormattedCSV = $rawCSV |
Select-Object $SelectColumns
This was hand-copied to a computer where I don't have the rights to run it, so there might be an error - I tried to copy it correctly
You can use gocsv https://github.com/DataFoxCo/gocsv to see the headers of the csv, you can then rename the headers, behead the file, swap columns, join, merge, any number of transformations you want

Remove blank lines Powershell output

I wrote a script to find GID's in AD, this is working fine, the issue I'm having is filtering out the blank (null lines)
$searcher=[adsisearcher]"(objectCategory=user)"
$result = $searcher.FindAll()
$result | Select-Object #{Name="DN";Expression+{$_.properties.distinguishedname}},#{Name="gid";Expression={$_.properties.gidnumber }} |Sort-Object -Property gid
I find it odd that you would be getting blank lines with that code. Can't think of a scenario where the distinguishedname of a user is null. The one issue with your code that I do see might just be a typo in your first calculated expression:
#{Name="DN";Expression+{$_.properties.distinguishedname}}
should instead be
#{Name="DN";Expression={$_.properties.distinguishedname}}
However that should have just made a syntax error that would be easily caught before execution.
Filtering Blanks
A real easy PowerShell way to deal with this is to use a simple Where-Object clause. For argument sake lets say that the GID could be empty/null.
$result | Select-Object #{Name="DN";Expression={$_.properties.distinguishedname}},#{Name="gid";Expression={$_.properties.gidnumber }} | Where-Object{$_.GID} | Sort-Object -Property gid
A null or empty value evaluates to False in PowerShell. Where-Object{$_.GID} will only allow objects with a populated property for GID to pass as output. You will get similar results from string static methods as well. These would also add readability to your code.
... | Where-Object{[string]::IsNullOrWhiteSpace($_.GID)} | ...
There is also [string]::IsNullOrEmpty()
Sort Object has a -unique property which will remove most of the blank lines, all but one as you have guessed. You could also pipe to a
where-object -ne '`n'
or something similar, let me know if I should elaborate that.