Look for a specific value in a csv file powershell - powershell

so i'm kinda new to PS, I've spent the whole morning trying to figure this out and looking for similar questions/answers here and on Google.
Basically this is my script:
$value = $env:COMPUTERNAME
$csv = Import-Csv -Path '\\UNC\PATH\file.csv'
$props = 'CsSystemFamily','CsDNSHostName', 'CsManufacturer'
Get-ComputerInfo | Select-Object -Property $props | Export-Csv -Path $csv -NoTypeInformation - Delimiter ';' -Append
i'm deploying this as GPO since i need to collect this specific data from some OU's. The thing is i want to check first if the computername exists or not in the CsDNSHostName column so that my script wont add the same computer twice.
Thanks in advance,
I've tried multiple things, but the last thing i found was this:
$value = $env:COMPUTERNAME
if ($file-contains $value) {
write-host 'Computer name already exists'
} else {
Get-ComputerInfo | Select-Object -Property $props | Export-Csv -Path $csv -NoTypeInformation - Delimiter ';' -Append
}
this didn't semm to work since it would just skip if and go straight to else

-contains is the right operator to use, but you must apply it to the array of CsDNSHostName property (column) values, which is easy to do, thanks to member-access enumeration:
$props = 'CsSystemFamily','CsDNSHostName', 'CsManufacturer'
$csvFile = '\\UNC\PATH\file.csv'
$csv = Import-Csv $csvFile -Delimiter ';'
# $csv.CsDNSHostName returns an array of
# all CsDNSHostName column (property) values.
if ($csv.CsDNSHostName -contains $env:COMPUTERNAME) {
Write-Host 'Computer name already exists'
} else {
Get-ComputerInfo |
Select-Object -Property $props |
Export-Csv $csvFile -NoTypeInformation -Delimiter ';' -Append
}

Related

Powershell - Combine CSV files and append a column

I'm trying (badly) to work through combining CSV files into one file and prepending a column that contains the file name. I'm new to PowerShell, so hopefully someone can help here.
I tried initially to do the well documented approach of using Import-Csv / Export-Csv, but I don't see any options to add columns.
Get-ChildItem -Filter *.csv | Select-Object -ExpandProperty FullName | Import-Csv | Export-Csv CombinedFile.txt -UseQuotes Never -NoTypeInformation -Append
Next I'm trying to loop through the files and append the name, which kind of works, but for some reason this stops after the first row is generated. Since it's not a CSV process, I have to use the switch to skip the first title row of each file.
$getFirstLine = $true
Get-ChildItem -Filter *.csv | Where-Object {$_.Name -NotMatch "Combined.csv"} | foreach {
$filePath = $_
$collection = Get-Content $filePath
foreach($lines in $collection) {
$lines = ($_.Basename + ";" + $lines)
}
$linesToWrite = switch($getFirstLine) {
$true {$lines}
$false {$lines | Select -Skip 1}
}
$getFirstLine = $false
Add-Content "Combined.csv" $linesToWrite
}
This is where the -PipelineVariable parameter comes in real handy. You can set a variable to represent the current iteration in the pipeline, so you can do things like this:
Get-ChildItem -Filter *.csv -PipelineVariable File | Where-Object {$_.Name -NotMatch "Combined.csv"} | ForEach-Object { Import-Csv $File.FullName } | Select *,#{l='OriginalFile';e={$File.Name}} | Export-Csv Combined.csv -Notypeinfo
Merging your CSVs into one and adding a column for the file's name can be done as follows, using a calculated property on Select-Object:
Get-ChildItem -Filter *.csv | ForEach-Object {
$fileName = $_.Name
Import-Csv $_.FullName | Select-Object #{
Name = 'FileName'
Expression = { $fileName }
}, *
} | Export-Csv path/to/merged.csv -NoTypeInformation

powershell foreach shows duplicate result

I use powershell to automate extracting of selected data from a CSV file.
My $target_servers also contains two the same server name but it has different data in each rows.
Here is my code:
$target_servers = Get-Content -Path D:\Users\Tools\windows\target_prd_servers.txt
foreach($server in $target_servers) {
Import-Csv $path\Serverlist_Template.csv | Where-Object {$_.Hostname -Like $server} | Export-Csv -Path $path/windows_prd.csv -Append -NoTypeInformation
}
After executing the above code it extracts CSV data based on a TXT file, but my problem is some of the results are duplicated.
I am expecting around 28 results but it gave me around 49.
As commented, -Append is the culprit here and you should check if the newly added records are not already present in the output file:
# read the Hostname column of the target csv file as array to avoid duplicates
$existingHostsNames = #((Import-Csv -Path "$path/windows_prd.csv").Hostname)
$target_servers = Get-Content -Path D:\Users\Tools\windows\target_prd_servers.txt
foreach($server in $target_servers) {
Import-Csv "$path\Serverlist_Template.csv" |
Where-Object {($_.Hostname -eq $server) -and ($existingHostsNames -notcontains $_.HostName)} |
Export-Csv -Path "$path/windows_prd.csv" -Append -NoTypeInformation
}
You can convert your data to array of objects and then use select -Unique, like this:
$target_servers = Get-Content -Path D:\Users\Tools\windows\target_prd_servers.txt
$data = #()
foreach($server in $target_servers) {
$data += Import-Csv $path\Serverlist_Template.csv| Where-Object {$_.Hostname -Like $server}
}
$data | select -Unique | Export-Csv -Path $path/windows_prd.csv -Append -NoTypeInformation
It will work only if duplicated rows have same value in every column. If not, you can pass column names to select which are important for you. For ex.:
$data | select Hostname -Unique | Export-Csv -Path $path/windows_prd.csv -Append -NoTypeInformation
It will give you list of unique hostnames.

Filtering Data By Property

I'm seraching for a solution to filter entries in a csv in Powershell
My File looks like this
Header1;Header2;Header3
Tom;15;15.12.2008
Anna;17;
Tim;18;12.01.2007
My Code looks atm like this :
$altdaten = Get-Content -Path $altdatenpf | Select-Object -skip 1 |`
ConvertFrom-Csv `
-Delimiter ";"`
-Header $categoriesCSV
$neudaten = Get-Content -Path $neudatenpf | Select-Object -skip 1 |`
ConvertFrom-Csv `
-Delimiter ";"`
-Header $categoriesCSV
$zdaten = foreach ($user in $neudaten)
{
Where-Object $user.Austrittsdatum -EQ ''
}
$zdaten | export-Csv -Path '.\Test\zwischendaten.csv'
In this case i want delete all entrys that are like tim and Tom, they have entrys in header3
Thank you in advance
You can try something like this:
$altdaten = Get-Content -Path .\pathToCsvFile.csv | ConvertFrom-Csv -Delimiter ';'
$zdaten = $altdaten | Where-Object { $_.Header3 -eq $null }
Your equality operator checks actually an empty string. However, in your CSV file, there is nothing. Hence, you need to check for $null.
Alternatively, if you are not sure, you can use:
$zdaten = $altdaten | Where-Object { [string]::IsNullOrEmpty($_.Header3) }
This covers either option and also looks appealing (for me at least).

Check csv file values against every line in another csv file

I have two csv file where I contain data, I need to check if value from CSV 1 exist in CSV 2 and if so then replace this value in file2 with data from file1, if no just skip to another row,
File1.csv
NO;Description
L001;DREAM
L002;CAR
L003;PHONE
L004;HOUSE
L005;PLANE
File2.csv
ID;Name;Status*;Scheduled Start Date;Actual Start Date;Actual End Date;Scheduled End Date;SLA
144862;DREAM;Scheduled;1524031200;;;1524033000;
149137;CAR;Implementation In Progress;1528588800;;;1548968400;
150564;PHONE;Scheduled;1569456000;;;1569542400;
150564;HOUSE;Scheduled;1569456000;;;1569542400;
150564;PLANE;;;;;;
I tried something like that but it is not working for me:
$file1 = Import-Csv "C:\Users\file1.csv" |Select-Object -ExpandProperty Description
$file2 = Import-Csv "C:\Users\file1.csv" |Select-Object -ExpandProperty NO
Import-Csv "C:\Users\file3.csv" |Where-Object {$file1 -like $_.Name} |ForEach-Object {
$_.Name = $file2($_.NO)
} |Out-File "C:\Users\File4.csv"
File4.csv should like that:
ID;Name;Status*;Scheduled Start Date;Actual Start Date;Actual End Date;Scheduled End Date;SLA
144862;L001;Scheduled;1524031200;;;1524033000;
149137;L002;Implementation In Progress;1528588800;;;1548968400;
150564;L003;Scheduled;1569456000;;;1569542400;
150564;L004;Scheduled;1569456000;;;1569542400;
150564;L005;;;;;;
Maybe there is another way to achive my goal! Thank you
Here's one approach you can take:
Import both CSV files with Import-Csv
Create a lookup hash table from the first CSV file, where the Description you want to replace are the keys, and NO are the values.
Go through the second CSV file, and replace any values from the Name column from the hash table, if the key exists. We can use System.Collections.Hashtable.ContainsKey to check if the key exists. This is a constant time O(1) operation, so lookups are fast.
Then we can export the final CSV with Export-Csv. I used -UseQuotes Never to put no " quotes in your output file. This feature is only available in PowerShell 7. For lower PowerShell versions, you can have a look at How to remove all quotations mark in the csv file using powershell script? for other alternatives to removing quotes from a CSV file.
Demo:
$csvFile1 = Import-Csv -Path .\File1.csv -Delimiter ";"
$csvFile2 = Import-Csv -Path .\File2.csv -Delimiter ";"
$ht = #{}
foreach ($item in $csvFile1) {
if (-not [string]::IsNullOrEmpty($item.Description)) {
$ht[$item.Description] = $item.NO
}
}
& {
foreach ($line in $csvFile2) {
if ($ht.ContainsKey($line.Name)) {
$line.Name = $ht[$line.Name]
}
$line
}
} | Export-Csv -Path File4.csv -Delimiter ";" -NoTypeInformation -UseQuotes Never
Or instead of wrapping the foreach loop inside a script block using the Call Operator &, we can use Foreach-Object. You can have a look at about_script_blocks for more information about script blocks.
$csvFile2 | ForEach-Object {
if ($ht.ContainsKey($_.Name)) {
$_.Name = $ht[$_.Name]
}
$_
} | Export-Csv -Path File4.csv -Delimiter ";" -NoTypeInformation -UseQuotes Never
File4.csv
ID;Name;Status*;Scheduled Start Date;Actual Start Date;Actual End Date;Scheduled End Date;SLA
144862;L001;Scheduled;1524031200;;;1524033000;
149137;L002;Implementation In Progress;1528588800;;;1548968400;
150564;L003;Scheduled;1569456000;;;1569542400;
150564;L004;Scheduled;1569456000;;;1569542400;
150564;L005;;;;;;
Update
For handling multiple values with the same Name, we can transform the above to use a hash table of System.Management.Automation.PSCustomObject, where we have two properties Count to keep track of the current item we're seeing and NO which is an array of numbers:
$csvFile1 = Import-Csv -Path .\File1.csv -Delimiter ";"
$csvFile2 = Import-Csv -Path .\File2.csv -Delimiter ";"
$ht = #{}
foreach ($row in $csvFile1) {
if (-not $ht.ContainsKey($row.Description) -and
-not [string]::IsNullOrEmpty($item.Description)) {
$ht[$row.Description] = [PSCustomObject]#{
Count = 0
NO = #()
}
}
$ht[$row.Description].NO += $row.NO
}
& {
foreach ($line in $csvFile2) {
if ($ht.ContainsKey($line.Name)) {
$name = $line.Name
$pos = $ht[$name].Count
$line.Name = $ht[$name].NO[$pos]
$ht[$name].Count += 1
}
$line
}
} | Export-Csv -Path File4.csv -Delimiter ";" -NoTypeInformation -UseQuotes Never
If your files aren't too big, you could do this with a simple ForEach-Object loop:
$csv1 = Import-Csv -Path 'D:\Test\File1.csv' -Delimiter ';'
$result = Import-Csv -Path 'D:\Test\File2.csv' -Delimiter ';' |
ForEach-Object {
$name = $_.Name
$item = $csv1 | Where-Object { $_.Description -eq $name } | Select-Object -First 1
# update the Name property and output the item
if ($item) {
$_.Name = $item.NO
# if you output the row here, the result wil NOT contain rows that did not match
# $_
}
# if on the other hand, you would like to retain the items that didn't match unaltered,
# then output the current row here
$_
}
# output on screen
$result | Format-Table -AutoSize
#output to new CSV file
$result | Export-Csv -Path 'D:\Test\File4.csv' -Delimiter ';' -NoTypeInformation
Result on screen:
ID Name Status* Scheduled Start Date Actual Start Date Actual End Date Scheduled End Date SLA
-- ---- ------- -------------------- ----------------- --------------- ------------------ ---
144862 L001 Scheduled 1524031200 1524033000
149137 L002 Implementation In Progress 1528588800 1548968400
150564 L003 Scheduled 1569456000 1569542400
150564 L004 Scheduled 1569456000 1569542400
150564 L005

Add or Change Values to CSV

I import a CSV in PowerShell, and want to export it again by either adding or changing content.
That's how I import it:
$csv = Import-Csv $csvFile -Delimiter ";"
The CSV has three colums, UserName, Pass and Key.
When I want to change one row:
$csv = $csv | ? { $_.UserName -eq $UserName } | % {
$_.pass = $PasName
$_.key = $KeyName
}
$csv | Export-Csv $csvFile -Delimiter ";" -Force -NoTypeInformation
it leaves me with a CSV without any content. Why is that?
Also, when I just want to add one row
$csv += [PSCustomObject]#{
UserName = $UserName
pass = $PasName
key = $KeyName
}
$csv | Export-Csv $csvFile -Delimiter ";" -Force -NoTypeInformation
it returns an error, saying:
Fehler beim Aufrufen der Methode, da [System.Management.Automation.PSObject] keine Methode mit dem Namen "op_Addition" enthält.
In Zeile:44 Zeichen:13
Does anybody know why this happens?
I know I could just add Content like so:
$xy = "{0};{1};{2}" -f xy,xy,xy
Add-Content -Path $csvFile -Value $xy
but I'd rather go with exporting the CSV completely new
Your second code snippet leaves you with an empty result because you assign the loop output back to the variable $csv. However, the loop does not produce any output, so you're effectively clearing the variable by doing that. To fix this simply remove the assignment. The loop updates the variable in-place.
$csv | Where-Object {
$_.UserName -eq $UserName
} | ForEach-Object {
$_.pass = $PasName
$_.key = $KeyName
}
$csv | Export-Csv $csvFile -Delimiter ";" -Force -NoTypeInformation
The error from your 3rd code snippet can occur if $csv is not an array (e.g. when it's empty or contains just a single record). You can mitigate that by forcing it to be an array upon import:
$csv = #(Import-Csv $csvFile -Delimiter ";")
Also, for appending a new record to your CSV it's cleaner and more straightforward to actually append to the CSV:
[PSCustomObject]#{
UserName = $UserName
pass = $PasName
key = $KeyName
} | Export-Csv $csvFile -Delimiter ";" -Force -NoTypeInformation -Append