Notice: There is now a revisited, or second, Edit a CSV post. This time, however, we are obtaining and writing portions of a REST API response to our CSV.
I recently completed a scripting assignment for work. Yeah… it’s a part of what I do. During the process I learned something new, that I hadn’t known before. And like it normally does, this leaves me in a position to share it with the Internet, as well as help solidify it in my own mind. And that’s, why I’m writing today.
I thought my recent assignment had a need to edit an existing CSV file on the fly. It turns out it wasn’t necessary for the project, but just maybe it will be for another one, or maybe even one that’s sitting in front of you right now. So, how do you do that? How do you edit an existing CSV file?
Let’s begin with a simple, yet worthy CSV file as an example. In the below CSV data, we have four columns, fields, or properties — whatever you want to call them at this point. They are Name, Status, BatchName, and SentNotification. The idea here — or what I thought it was in my recent assignment, at first — was to notify by email the users that were in a Synced state (Status column) and then modify the SentNotification field so that it said True, instead of False. Take a look at our example data.
Name,Status,BatchName,SentNotification landrews,Other,MigrationService:FirstBatch-to-O365,FALSE lpope,Synced,MigrationService:FirstBatch-to-O365,FALSE ljohnson,Other,MigrationService:FirstBatch-to-O365,FALSE mlindsay,Other,MigrationService:FirstBatch-to-O365,FALSE rperkins,Synced,MigrationService:FirstBatch-to-O365,FALSE dstevenson,Other,MigrationService:FirstBatch-to-O365,FALSE jbradford,Other,MigrationService:FirstBatch-to-O365,FALSE jsmith,Other,MigrationService:FirstBatch-to-O365,FALSE mdavidson,Synced,MigrationService:FirstBatch-to-O365,FALSE bclark,Synced,MigrationService:FirstBatch-to-O365,FALSE
Let’s first begin by using Import-Csv and Format-Table to view our data.
Import-Csv -Path '.\Desktop\UCSVTestFile.csv' | Format-Table -AutoSize Name Status BatchName SentNotification ---- ------ --------- ---------------- landrews Other MigrationService:FirstBatch-to-O365 FALSE lpope Synced MigrationService:FirstBatch-to-O365 FALSE ljohnson Other MigrationService:FirstBatch-to-O365 FALSE mlindsay Other MigrationService:FirstBatch-to-O365 FALSE rperkins Synced MigrationService:FirstBatch-to-O365 FALSE dstevenson Other MigrationService:FirstBatch-to-O365 FALSE jbradford Other MigrationService:FirstBatch-to-O365 FALSE jsmith Other MigrationService:FirstBatch-to-O365 FALSE mdavidson Synced MigrationService:FirstBatch-to-O365 FALSE bclark Synced MigrationService:FirstBatch-to-O365 FALSE
Now what we need to do, is modify the content, as it’s being imported. Let’s start first, however, by piping our Import-Csv cmdlet to ForEach-Object and returning each object (line).
Import-Csv -Path '.\Desktop\UCSVTestFile.csv' | ForEach-Object { $_ } Name Status BatchName SentNotification ---- ------ --------- ---------------- landrews Other MigrationService:FirstBatch-to-O365 FALSE lpope Synced MigrationService:FirstBatch-to-O365 FALSE ljohnson Other MigrationService:FirstBatch-to-O365 FALSE mlindsay Other MigrationService:FirstBatch-to-O365 FALSE rperkins Synced MigrationService:FirstBatch-to-O365 FALSE dstevenson Other MigrationService:FirstBatch-to-O365 FALSE jbradford Other MigrationService:FirstBatch-to-O365 FALSE jsmith Other MigrationService:FirstBatch-to-O365 FALSE mdavidson Synced MigrationService:FirstBatch-to-O365 FALSE bclark Synced MigrationService:FirstBatch-to-O365 FALSE
Hey, look at that. It’s the same thing. The $_ variable represents the current object, or the current row, — if it helps to think about it that way — in the pipeline. Let’s add an If statement inside our ForEach-Object loop, and get the results we’re after. Remember, if a user has a Synced status, we want to change their SentNotification property to $true, and perhaps notify them, had this been more than just an example.
Import-Csv -Path '.\Desktop\UCSVTestFile.csv' | ForEach-Object { If ($_.Status -eq 'Synced' -and $_.SentNotification -eq $false) { $_.SentNotification = $true } $_ } | Format-Table -AutoSize Name Status BatchName SentNotification ---- ------ --------- ---------------- landrews Other MigrationService:FirstBatch-to-O365 FALSE lpope Synced MigrationService:FirstBatch-to-O365 True ljohnson Other MigrationService:FirstBatch-to-O365 FALSE mlindsay Other MigrationService:FirstBatch-to-O365 FALSE rperkins Synced MigrationService:FirstBatch-to-O365 True dstevenson Other MigrationService:FirstBatch-to-O365 FALSE jbradford Other MigrationService:FirstBatch-to-O365 FALSE jsmith Other MigrationService:FirstBatch-to-O365 FALSE mdavidson Synced MigrationService:FirstBatch-to-O365 True bclark Synced MigrationService:FirstBatch-to-O365 True
In the above example, we use an If statement to check the values of two properties. If Status is Synced and SentNotification is $false, we’ll change SentNotification to $true. You can see that this worked. But what now? You see, the file from which we did our import is still the same. In order to update that file, we have a bit more work to do.
I wish I could say pipe directly back to the file; however, that doesn’t work. The file ends up being blank. It makes sense it doesn’t work though, as we’re literally reading each object — each row — and then trying to write back to the file in the same pipeline. Something is bound to go wrong, and it does. So, don’t do what’s in the below example, unless your goal is to fail at this assignment and wipe out your data. If that’s what you’re after, then by all means, have at it.
Import-Csv -Path '.\Desktop\UCSVTestFile.csv' | ForEach-Object { If ($_.Status -eq 'Synced' -and $_.SentNotification -eq $false) { $_.SentNotification = $true } $_ } | Export-Csv -Path '.\Desktop\UCSVTestFile.csv' -NoTypeInformation
What we need to do instead, is Export to a file with a different name, so that when we’re done, both files exist at the same time. Then, we remove the original file and rename the new one with the old one’s name. Here’s the entire example; take a look. And then after that, enjoy the weekend. Oh wait, tomorrow is only Friday. I keep thinking it’s the weekend, because I’m home tomorrow to deal with 1,000 square feet of sod. If only PowerShell could lay the sod for me.
Import-Csv -Path '.\Desktop\UCSVTestFile.csv' | ForEach-Object { If ($_.Status -eq 'Synced' -and $_.SentNotification -eq $false) { $_.SentNotification = $true } $_ } | Export-Csv -Path '.\Desktop\UCSVTestFile-temp.csv' -NoTypeInformation Remove-Item -Path '.\Desktop\UCSVTestFile.csv' Rename-Item -Path '.\Desktop\UCSVTestFile-temp.csv' -NewName 'UCSVTestFile.csv'
Great post! I used it to to take students out of CSV that was 76K lines. Straight and to the point. Thanks.