powershell foreach shows duplicate result - powershell

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.

Related

Look for a specific value in a csv file 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
}

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 CSV, take a specific row from each line and combine it into one CSV

I have 300 CSV files all separated in a directory.
I want to get one specific criteria from each CSV and put it into another using PowerShell.
This is the line I have, but doesn't seem to work.
Get-ChildItem -Filter "*Results.csv" | Get-Content | Where-Object {$_.NAME -eq "Cage,Johnny"} | Add-Content "test.csv"
I filtered for the specific CSVs I wanted in my directory with gci, Got the content of each using Get-Content and Where the value is Johnny Cage in the NAME column, and Add-Content into a test.csv file but doesn't work.
Any help would be great!
You need to deserialize your CSV text into objects with properties that can be referenced. Then you can compare the Name property. You can do the following if all your csv files have the same headers.
Get-ChildItem -Filter "*Results.csv" | Foreach-Object {
Import-Csv $_.FullName |
Where-Object {$_.NAME -eq "Cage,Johnny"} } |
Export-Csv "test.csv"
If your CSV files contain different headers, then you have a couple of options. One, you could create your output CSV with all possible headers that exist across all files (or just the headers you want as long as they are the same across all files). Second, you could just output your data rows and have a broken CSV.
# Broken CSV Approach
Get-ChildItem -Filter "*Results.csv" | Foreach-Object {
Import-Csv $_.FullName |
Where-Object {$_.NAME -eq "Cage,Johnny"}} | Foreach-Object {
$_ | ConvertTo-Csv -Notype | Select-Object -Skip 1
} | Add-Content test.csv
I think I got it.
Get-ChildItem -Filter *Results.csv |
ForEach-Object{
Import-Csv $_.NAME | ? { $_.EMPLID -eq "Cage,Johnny"}
} | Export-Csv "test.csv"

How to merge and remove duplicates of CSV files using Powershell

I would like to remove duplicates in a CSV file using PowerShell. I know that there are posts about this already but I can't seem to find one that helps.
I'm trying to merge 2 CSV Files that have the same header and then remove the duplicates of the resulting file based on the IDs listed in the first column and then put it to the same CSV file.
The properties of the file are as follows:
And when I try to use the sort and unique method, I get the following (not a table:
Here is my code so far:
####
#MERGE
$getFirstLine = $true
get-childItem "C:\IGHandover\Raw\IG_INC*.csv"| foreach {
$filePath = $_
$lines = $lines = Get-Content $filePath
$linesToWrite = switch($getFirstLine) {
$true {$lines}
$false {$lines | Select -Skip 1}
}
$getFirstLine = $false
Add-Content "C:\IGHandover\new.csv" $linesToWrite
}
####
#REMOVE DUPLICATES
Import-Csv "C:\IGHandover\new.csv" | Sort inc_number -Unique |
Set-Content "C:\IGHandover\new.csv"
Don't use Get-Content or Set-Content to import or export csv file
Import-Csv (Get-ChildItem 'C:\IGHandover\Raw\IG_INC*.csv') |
Sort-Object -Unique inc_number |
Export-Csv 'C:\IGHandover\new.csv' -NoClobber -NoTypeInformation
I guess you want to update a table (HandoverINC.csv) with records from a new table (New.csv), replacing any records in the HandoverINC.csv with the same primary key (inc_number) from the New.csv in the HandoverINC.csv. And add any new records in the New.csv to the HandoverINC.csv (Basically what is called a Full Join in SQL).
Using the Join-Object described at: https://stackoverflow.com/a/45483110/1701026
Import-CSV .\HandoverINC.csv | FullJoin (Import-CSV .\New.csv) inc_number {$Right.$_} | Export-CSV .\HandoverINC.csv
As suggested by Lieven Keersmaekers and Vivek Kumar, I've made a few changes in my code:
Put the merged contents to a temporary file
Import the csv file with the merge contents
Sort the column of reference and use the unique parameter
Export the results to a new csv file
I found that my code was similar to Vincent K's:
#MERGE
$getFirstLine = $true
get-childItem "C:\IGHandover\Raw\IG_INC*.csv"|
foreach {
$filePath = $_
$lines = $lines = Get-Content $filePath
$linesToWrite = switch($getFirstLine) {
$true {$lines}
$false {$lines | Select -Skip 1}}
$getFirstLine = $false
Add-Content "C:\IGHandover\HandoverINCtemp.csv" $linesToWrite }
#REMOVE DUPLICATES
Import-Csv "C:\IGHandover\HandoverINCtemp.csv" | Sort inc_number -Unique |
Export-Csv "C:\IGHandover\HandoverINC.csv" -NoClobber -NoTypeInformation -Force
Remove-Item "C:\IGHandover\HandoverINCtemp.csv"
To simplify (merging and removing duplicates with the same header), as suggested by Vincent:
Import-Csv (Get-ChildItem "C:\IGHandover\Raw\IG_INC*.csv") | Sort inc_number -Unique |
Export-Csv "C:\IGHandover\HandoverINC.csv" -NoClobber -NoTypeInformation -Force
I hope this helps anyone who'd like to do the same with their files

Splitting CSV file by two columns

Starting with a 500,000 line CSV, I need to split the files by day and hour (the second and third columns). I've tried the modify the group to include the hour and while I see the hour get added to my filename, I get no results in the exported file.
The foreach doing the work:
foreach ($group in $data | Group Day,hour) {
$data | Where-Object { $_.Day -and $_.Hour -eq $group.Name }
ConvertTo-Csv -NoTypeInformation |
foreach {$_.Replace('"','')} |
Out-File "$Path\Testfile_$($group.name -replace $regexA, '').csv"
Sample Data:
Bob,1/27/2012,8:00,Basic,Operations
Charlie,2/3/2012,9:00,Advanced,Production
Bill,3/7/2012,10:00,Advanced,Production
You could import the CSV, determine the output filename on the fly, and append each record to the matchning file:
Import-Csv 'C:\path\to\input.csv' | ForEach-Object {
$filename = ('output_{0}_{1}.csv' -f $_.Day, $_.Hour) -replace '[/:]'
$_ | Export-Csv "C:\path\to\$filename" -Append -NoType
}
Note that Export-Csv -Append requires PowerShell v3 or newer.