Change and add data to CSV file - powershell

I have a script that should look in a CSV file and modify it.
My file looks like this:
"mydata","module1","module2","module3","module4","module5"
"kk-ll","module1","","module3","",""
"kk-pp","module1","","module3","",""
In case the data in column mydata exists: modify value in column $Module.
In case it does not exist: add a new line to the file.
The code I wrote for the fist part is OK, for the second part (updating the file) it's not working.
$ExistingCSV = Import-Csv MyCsv.csv
if ($ExistingCSV.mydata -eq "$LOT-$WID") {
$ExistingCSV | ForEach-Object {
if ($_.mydata -eq "$LOT-$WID") {
$_.$Module = $Module
}
}
$ExistingCSV | Export-Csv $ProgressLogPath\$LOT.csv -NoTypeInformation
} else {
$ExistingCSV.mydata = "$LOT-$WID"
$ExistingCSV.$Module = $Module
Add-Content $ExistingCSV -Value $_.Lot_WaferID $_.$Module $_.ScriptLogPath
$ExistingCSV | Export-Csv $ProgressLogPath\$LOT.csv -NoTypeInformation
}

I would use -contains instead of -eq, but since PowerShell operators work as enumerators the latter should work too in your case.
If a match is found, modify the existing data and export the result back to the CSV. Otherwise all you need to do is create a custom object with the data you want to add, and append that to the CSV.
$csv = 'C:\path\to\your.csv'
$ExistingCSV = Import-Csv $csv
if ($ExistingCSV.mydata -contains "$LOT-$WID") {
foreach ($row in $ExistingCSV) {
if ($row.mydata -eq "$LOT-$WID") {
$row.$Module = $Module
}
}
$ExistingCsv | Export-Csv $csv -NoType
} else {
$obj = New-Object -Type PSObject -Property #{
'mydata' = "$LOT-$WID"
'module1' = $null
'module2' = $null
'module3' = $null
'module4' = $null
'module5' = $null
}
$obj.$Module = $Module
$obj | Export-Csv $csv -NoType -Append
}
Replace $null with whatever value the moduleX fields are supposed to have.

Related

Import CSV with loop problem - The result contains only last item from the CSV

I need to import a few groups from a CSV file and then export members - but I need it like one row each, something like:
"Group_name1", "member1, member2,member3"
"Group_name2", "member1,member2,member3"
"Group_name3", "member1,member2,member3"
And my script is working fine for a single group but I'm having troubles with adding a for-each loop - the result contains only last item from the CSV..
#$DL='plum'
$DL_List = "C:\ps1\shared_mailboxes\groups.csv"
$DL_array = (Import-Csv -Path $DL_List).name
foreach ($DL in $DL_array)
{
$DL_Membership = (Get-DistributionGroupMember -identity $DL).displayName
if([string]$DL_Membership -ne "")
{
$Members = ""
foreach($DL_Membership in $DL_Membership)
{
if($Members -ne "")
{
$Members=$Members +","
}
$Members = $Members + $DL_Membership
}
}
}
$ExportCSV=".\group_members_$((Get-Date -format yyyy-MMM-dd-ddd` hh-mm` tt).ToString()).csv"
$Result = #{'Group'=$DL;'Users'=$Members}
$Results = New-Object PSObject -Property $Result
$Results |select-object 'Group','Users' | Export-Csv -Path $ExportCSV -Notype -Append
I googled it but I'm not sure what I should change in my script..
The script has been re-writed using -join operator and now it's working as excepted.
$DL_List = Import-Csv "C:\ps1\shared_mailboxes\groups.csv"
$CsvOut = foreach ( $dl in $DL_List ) {
$Group = $dl.name
$MemberArray = (Get-DistributionGroupMember -identity $Group).displayName
$MemberList = $MemberArray -join ','
# generate the output object
# that goes in $CsvOut
[PSCustomObject] #{
Group = $Group
Members = $MemberList
}
}
$Timestamp = Get-Date -Format 'yyyy-MM-dd-HHmm' # this is a string
$CsvOutPath = "c:\ps1\shared_mailboxes\memberlist.$Timestamp.csv"
$CsvOut | Export-Csv $CsvOutPath -NoTypeInfo
I think you are over-complicating this.
If your desired result is a CSV file, then output objects straight away like below:
$DL_List = 'C:\ps1\shared_mailboxes\groups.csv'
$DL_array = (Import-Csv -Path $DL_List).name
$outFile = '.\group_members_{0:yyyy-MMM-dd-ddd hh-mm}.csv' -f (Get-Date)
$result = foreach ($DL in $DL_array) {
# output an object with the name of the DL and its members
[PsCustomObject]#{
Group = $DL
Users = (($DL | Get-DistributionGroupMember).displayName |
Where-Object {$_ -match '\S'}) -join ','
}
}
$result | Export-Csv -Path $outFile -NoTypeInformation
Using calculated properties, you can also do this without a foreach loop:
$DL_List = 'C:\ps1\shared_mailboxes\groups.csv'
$DL_array = (Import-Csv -Path $DL_List).name
$outFile = '.\group_members_{0:yyyy-MMM-dd-ddd hh-mm}.csv' -f (Get-Date)
$DL_array | Select-Object #{Name = 'Group'; Expresssion = {$_}},
#{Name = 'Users'; Expresssion = {
(($_ | Get-DistributionGroupMember).displayName |
Where-Object {$_ -match '\S'}) -join ','
}} |
Export-Csv -Path $outFile -NoTypeInformation

If Else statement Powershell CSV with Output CSV

I am fairly new in powershell scripting and need help on the following output in a csv format. I am trying to select a column e.g. ACCOUNT.UPLOAD and make and if/else statement to output it in another csv file. May someone help please.
Output csv should look like below:
$results = Import-Csv 'C:\Users\test\Desktop\Test\customer.csv' |
Select-Object "ACCOUNT.UPLOAD"
ForEach ($row in $results)
{
If ($row.Type0 -ne 'CP000101', 'CP000102')
{
$row."ACCOUNT.UPLOAD" = "$($row.ACCOUNT.UPLOAD)"
Write-Host $row."ACCOUNT.UPLOAD"
}
}
$results | Export-Csv C:\Users\test\Desktop\Test\test.csv -NoTypeInformation
Thank you
This will get you what you need. Added comments to explain what I have done.
$results = Import-Csv "C:\Users\test\Desktop\Test\customer.csv" | Select-Object "ACCOUNT.UPLOAD"
# Created array to be able to add individual results from foreach
$TheCSV = #()
ForEach ($row in $results) {
# You can use a -ne in the right hand side, if done like this.
If (($row.'ACCOUNT.UPLOAD' -ne 'CP000101') -and $row.'ACCOUNT.UPLOAD' -ne 'CP000102') {
# Adds the ROW column to the entry and finds the index that it was in from $results.
# Did a +2 as it does not include the header and it starts at value 0. So to match it up with the actual excel row numbers, add 2.
$row | Add-Member -Name 'ROW' -type NoteProperty -Value "$([array]::IndexOf($results.'ACCOUNT.UPLOAD',$row.'ACCOUNT.UPLOAD')+2)"
$TheCSV += $row
}
}
$TheCSV | Export-Csv "C:\Users\test\Desktop\Test\test.csv" -NoTypeInformation
Do it PowerShell way:
param(
[Parameter(Position=0)]
$InputFile = 'D:\\Temp\\Data.csv',
[Parameter(Position=1)]
$OutputFile = 'D:\\Temp\\Output.csv'
)
Import-Csv $InputFile |
Select-Object "ACCOUNT.UPLOAD" |
%{
$lineno++
if ($_.'ACCOUNT.UPLOAD' -notin #('CP000101', 'CP000102')) {
$_ | Add-Member -Name 'ROW' -type NoteProperty -Value $lineno
$_ # Output to pipeline
}
} -Begin { $lineno = 1 } |
Export-Csv $OutputFile -NoTypeInformation
Using:
.\Script.ps1
.\Script.ps1 inputfilename.csv outputfilefname.csv

Reformat CSV file in PowerShell

I have a well-formed CSV file that I want to read, reformatting some values while doing so. For some reason the code below does not work ($csv variable ends up being empty):
$csv = Import-Csv "result.log" -Delimiter "|" | ForEach-Object {
$_.PSObject.Properties.Value | ForEach-Object {
if ($_ -like '*---*') { $_ = "" }
}
}
What am I doing wrong?
Your result is blank because you aren't returning anything to the pipeline (you are just changing the value of one or more properties but then not outputting them).
There might be a simpler solution but I think this achieves what you want:
$CSV = Import-Csv "result.log" -Delimiter "|" | Foreach-Object {
$Output = New-Object -TypeName PSObject
$_.PSObject.Properties | Foreach-Object {
If ($_.Value -like '*---*') { $_.Value = '' }
$Output | Add-Member -Name $_.Name -Value $_.Value -MemberType NoteProperty
}
$Output
}
This performs the following for each row of the CSV:
creates a new empty PowerShell object
loops through the properties of the CSV, blanking ones that include --- and then uses Add-Member to add those properties to our object
outputs the new object to the standard pipeline
The result of this goes to the $CSV variable which you then could output as CSV via Export-CSV (or you could skip putting it in the variable and use Export-CSV on the end of the outer ForEach-Object).

Sorting using the sort object in PowerShell part 3

Changed my code a little from part 2 example below but taking part 2 example which works great thanks to a member in stackoverflow.
[string] $Source = 'e:\Temp\DataFromSkywardEdited.csv';
[string] $Destination = 'e:\Temp\DataFromSkywardRD.csv';
[object] $SortByColNames = 'LastName','FirstName';
[string] $ShowColsByNumber = "{0},{1},{2},{3},{4},{5},{6},{7},{8}";
[object] $ColumnValueFormat = '$_.EmployeeID.Trim()', '$_.FirstName.Trim()', '$_.LastName.Trim()', '$_.Location.Trim()', '$_.Department.Trim()', '$_.TelephoneNo.Trim()', '$_.Email.Trim()', '$_.EmpTypeCode.Trim()', '$_.EmployeeTypeDescription.Trim()';
Get-Content $Source |
ConvertFrom-Csv -Delimiter $Delimiter |
Sort-Object -Property $SortByColNames -Unique |
ForEach-Object {
# Each of the values in $ColumnValueFormat must be executed to get the property from the loop variable ($_).
$values = foreach ($value in $ColumnValueFormat) {
Invoke-Expression $value
}
# Then the values can be passed in as an argument for the format operator.
$ShowColsByNumber -f $values
} |
Add-Content $Destination
How would I make $ColumnValueFormat to be represented by numbers instead of a column name?
So instead of:
[object] $ColumnValueFormat = '$_.EmployeeID.Trim()', '$_.FirstName.Trim()', '$_.LastName.Trim()', '$_.Location.Trim()', '$_.Department.Trim()', '$_.TelephoneNo.Trim()', '$_.Email.Trim()', '$_.EmpTypeCode.Trim()', '$_.EmployeeTypeDescription.Trim()';
To something like:
[object] $ColumnValueFormat = '$_.[0].Trim()', '$_.[1].Trim()', '$_.[2].Trim()', '$_.[3].Trim()', '$_.[4].Trim()', '$_.[5].Trim()', '$_.[6].Trim()', '$_.[7].Trim()', '$_.[8].Trim()';
This really looks like you're doing it the hard way, but to directly answer your question, you would simply remove the period that preceeds the bracketed numbers. So...
[object] $ColumnValueFormat = '$_[0].Trim()', '$_[1].Trim()', '$_[2].Trim()', '$_[3].Trim()', '$_[4].Trim()', '$_[5].Trim()', '$_[6].Trim()', '$_.[7]Trim()', '$_[8].Trim()';
How would I make $ColumnValueFormat to be represented by numbers instead of a column name?
You wouldn't. Just … no.
To trim the fields of a CSV you'd simply do something like this:
$csv = Import-Csv $Source
foreach ($record in $csv) {
foreach ($property in $record.PSObject.Properties) {
$property.Value = $property.Value.Trim()
}
}
$csv | Sort-Object $SortByColNames -Unique | Export-Csv $Destination
If you had to treat different fields in different ways you'd use a switch statement to distinguish between the values:
$property.Value = switch ($property.Name) {
'foo' { ... }
'bar' { ... }
...
}
modify the fields via calculated properties:
Import-Csv $Source |
Select-Object #{n='EmployeeID';e={$_.EmployeeID.Trim()},
#{n='FirstName';e={...},
... |
Sort-Object $SortByColNames -Unique |
Export-Csv $Destination
or re-create the records via New-Object:
Import-Csv $Source |
ForEach-Object {
New-Object -Type PSObject -Property #{
'EmployeeID' = $_.EmployeeID.Trim()
'FirstName' = ...
...
}
} |
Sort-Object $SortByColNames -Unique |
Export-Csv $Destination
In PowerShell v3 and newer you can even use the type accelerator [ordered] to have properties created in a particular order, like this:
$props = [ordered]#{
'EmployeeID' = $_.EmployeeID.Trim()
'FirstName' = ...
...
}
New-Object -Type PSObject -Property $props
or like this:
[PSObject][ordered]#{
'EmployeeID' = $_.EmployeeID.Trim()
'FirstName' = ...
...
}

Merge 2 CSV by Value "Name" and merge Count (Powershell)

So at the momemt I'm searching for a way to merge 2 CSV files.
Here is an example for what I mean:
CSV1
"Name","Count"
"Klaus","3"
"Hans","2"
"Gerhard","1"
"Nina","6"
"Julia","10"
"Caro","19"
CSV2
"Name","Count"
"Klaus","2"
"Hans","1"
"Gerhard","1"
"Nina","1"
Now if I merge both, the output/result should be:
"Name","Count"
"Klaus","5"
"Hans","3"
"Gerhard","2"
"Nina","7"
"Julia","10"
"Caro","19"
I tried a lot, but I´ve never had suscess; I always had wrong results. Does anyone have an idea how to do this?
You can use Group-Object (alias group) to group everything by the Name property. Then you just need to sum up the Count property of each guy in the group. Measure-Object (alias measure) will do sums for you.
$grouped = Import-Csv .\csv1.csv, .\csv2.csv | group Name
$combined = $grouped |%{
New-Object PsObject -Prop #{ Name = $_.Name; Count = ($_.Group | measure -sum -prop Count).Sum }
}
$combined | Export-Csv .\combined.csv -NoType
Import the CSV files and convert each to a hash table, then find the common names:
$csv1 = Import-Csv -Path csv1.csv
$csv2 = Import-Csv -Path csv2.csv
$HashCSV1 = #{}
$HashCSV2 = #{}
$HashMerge = #{}
foreach($r in $csv1)
{
$HashCSV1[$r.Name] = $r.Count
}
foreach($r in $csv2)
{
$HashCSV2[$r.Name] = $r.Count
}
foreach ($key in $HashCSV1.Keys) {
if ($HashCSV2.ContainsKey($key)) {
$HashMerge[$key] = [int]$HashCSV1[$key] + [int]$HashCSV2[$key]
} else {
$HashMerge[$key] = $HashCSV1[$key]
}
}
foreach ($key in $HashCSV2.Keys) {
if (-not $HashCSV1.ContainsKey($key)) {
$HashMerge[$key] = $HashCSV2[$key]
}
}
&{$HashMerge.getenumerator() |
foreach {new-object psobject -Property #{Name = $_.name;Count=$_.value}}
} | export-csv merge.csv -notype