Modify a .csv file in powershell automatically - powershell

I try to create a powershell script, to perform a few steps:
In a specific folder, I put a .xlsx file, it converts it to csv. Until now I got this:
$ErrorActionPreference = 'Stop'
Function Convert-CsvInBatch
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)][String]$Folder
)
$ExcelFiles = Get-ChildItem -Path $Folder -Filter *.xlsx -Recurse
$excelApp = New-Object -ComObject Excel.Application
$excelApp.DisplayAlerts = $false
$ExcelFiles | ForEach-Object {
$workbook = $excelApp.Workbooks.Open($_.FullName)
$csvFilePath = $_.FullName -replace "\.xlsx$", ".csv"
$workbook.SaveAs($csvFilePath, [Microsoft.Office.Interop.Excel.XlFileFormat]::xlCSV)
$workbook.Close()
}
# Release Excel Com Object resource
$excelApp.Workbooks.Close()
$excelApp.Visible = $true
Start-Sleep 5
$excelApp.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excelApp) | Out-Null
}
#
# 0. Prepare the folder path which contains all excel files
$FolderPath = "C:\exacthpath"
Convert-CsvInBatch -Folder $FolderPath
The columns in the file, are still there, so I want to remove them, and insert a ';' instead, like:
H;1;43;185;
At this point I'm stuck. I can import it into Powershell like:
Import-Csv -Path 'C:\folder\filename.csv' | ForEach-Object {
$_
}
I get this look, and the most important task is here, in the first row only:
H;1;43;185;
This should be modified into:
H;01;43;185
the rest should be left untouched.
After I need to export back it into a CSV file, like:
Export-Csv -Path 'C:\folder\modified_filename.csv'
But this whole process should be inserted in one single powershell script, which performs the above steps on it's own. So in short:
identifies any .xlsx file - regardless of it's name
convers it into .csv
modifies the outlook of the document, to separate the columns with a ";"
modify the first line to have 'H;01;43;185' - this is a static line, it will always look like this
save the created file as a final .csv file
Can you help me somehow to include/optimize the above scripts and let powershell perform the modification too? Example content of a file like this (final look) Usually it includes more 1000+ lines:
H;01;43;185
D;111;3;1042;2
D;222;3;1055;3
D;333;3;1085;1
T;3;;;
Any help is highly appreciated.
Regards,
Armin

If as you say in your comment, your Excel already creates a csv with the semi-colon as delimiter, you can do this inside the loop, just below $workbook.Close()
# read the file created by Excel as string array
$data = Get-Content $csvFilePath
# overwrite the file with just the new header
Set-Content -Path $csvFilePath -Value 'H;01;43;185'
# add the rest of the data to the file
$data[1..($data.Count -1)] | Add-Content -Path $csvFilePath
P.S. I would delete the lines
$excelApp.Visible = $true
Start-Sleep 5
because I don't see the need to have Excel show itself and pause the function for 5 seconds.. Instead, have Excel not show at all so it will work a lot faster by adding
$excelApp.Visible = $false
right after you have created the $excelApp

Related

Call one Powershell script 2 from powershell script 1 with parameters

I have below PowerShell script which generate some set of CSV files from large CSV files
$BatchNr = 1
Import-Csv -Path .\Master.csv |Create-Batch -Size 50 |ForEach-Object {
$_ |ForEach-Object {
$_ # do something with each item in the batch of 50
} |Export-Csv ".\Batch$BatchNr.csv"
$BatchNr++
}
And results will be
Batch1.CSV,
Batch2.CSV and so on
Now I have another powershell script which do some operations with this Batch1.CSV,
Batch2.CSV. example below
$Users = Import-Csv -Path "Batch1.CSV"
But now i have to do it manually take each Batch. How can call this files from my PS1 file as soon as file create it, PS2 take files and execute it and after 15 minutes next files like this
Change the second script to accept the CSV path as a parameter:
# script2.ps1
param(
[string]$LiteralPath
)
$Users = Import-Csv -LiteralPath $LiteralPath
# ...
Then change the first script to pass the next CSV name to it:
$_ |ForEach-Object {
$_ # do something with each item in the batch of 50
} |Export-Csv ".\Batch$BatchNr.csv"
.\script2.ps1 -LiteralPath ".\Batch$BatchNr.csv"
$BatchNr++

UPDATE THE FIRST CELL IN A CSV FILE USING POWER SHELL SCRIPT

I already have this file Workspacesize.csv. I am adding a TEST value to cell (1,2) and trying to save. It is asking me for a prompt that the file already exists, do you want to overwrite. I donot want this prompt. I have used $Excelobject.DisplayAlerts= 'False' but still it does not work.
$Excelobject=New-object -ComObject Excel.Application
$Excelobject.visible = $False
$workbook=$Excelobject.Workbooks.Open("C:\Users\Siddhartha.S.Das2\OneDrive - Shell\Desktop\Workspacesize.csv")
$worksheet=$workbook.worksheets.Item(1)
$worksheet.Activate()
$worksheet.cells.item(1,2)="TEST"
$workbook.SaveAs("C:\Users\Siddhartha.S.Das2\OneDrive - Shell\Desktop\Workspacesize.csv")
$workbook.close
$Excelobject.DisplayAlerts= 'False'
$Excelobject.Quit()
You're better off not using excel for csv files, it unnecessarily complicates things.
$Path = "C:\Users\Siddhartha.S.Das2\OneDrive - Shell\Desktop\Workspacesize.csv"
$Content = Import-Csv -Path $Path
$Content[0].Col1 = 'TEST' #Put your actual column name rather than Col1
$Content | Export-Csv -Path $Path

Powershell - add multiple columns to multiple excel files

I have a folder that has over 50 excel files in it ("Project dump' in the path below.) All of these files contain the same exact data (its archived monthly data that's used for a MoM report) I need to update all of these files to add 10 new column headers - none of these columns will have any data in them, they just need to be added to the table to match the most current month extract that will have data in it going forward.
I've been using Powershell, and have a script that can add one column to one file at a time, but it would honestly be faster for me to manually open each file and add the columns myself. I cant seem to figure out how to change my script to do what its doing to multiple files (and with multiple columns), any help would be greatly appreciated!
background; the reference is a specific file in my project dump folder. Column 50 is the first blank column, that needs to be added to the table:
(Get-ChildItem "C:\Downloads\Project dump\ArchiveJAN21.xlsx")|
foreach-object {
$xl=New-Object -ComObject Excel.Application
$wb=$xl.workbooks.open($_)
$ws = $wb.worksheets.Item(1)
$ws.Columns.ListObject.ListColumns.Add(50)
$ws.Cells.Item(1,50) ='Call Type'
$wb.Save()
$xl.Quit()
while([System.Runtime.Interopservices.Marshal]::ReleaseComObject([System.__ComObject]$xl)){'released'| Out-Null}
}
You need to define the Excel object before the loop and quit afterwards.
Also, use Get-ChildItem to get FileInfo objects from a folder path, not a hardcoded path to a file.
Try:
# an array with the new column names
$newColumns = 'Call Type','NewCol2','NewCol3','NewCol4','NewCol5','NewCol6','NewCol7','NewCol8','NewCol9','NewCol10'
# create the Excel object outside of the loop
$xl = New-Object -ComObject Excel.Application
$xl.Visible = $false
# loop thtrough the files in the folder
(Get-ChildItem -Path 'C:\Downloads\Project dump' -Filter '*.xlsx' -File ) | ForEach-Object {
$wb = $xl.WorkBooks.Open($_.FullName)
$ws = $wb.Worksheets.Item(1)
# get the number of columns in the sheet
$startColumn = $ws.UsedRange.Columns.Count
for ($i = 0; $i -lt $newColumns.Count; $i++) {
$startColumn++ # increment the column counter
$ws.Cells.Item(1, $startColumn) = $newColumns[$i]
}
$wb.Close($true) # $true saves the changes
}
# quit Excel and clean COM objects from memory
$xl.Quit()
# clean up the COM objects used
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ws)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($xl)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

create csv from xls using powershell

I want create powershell script which create me csv file from .xls file but I don't know excacly how to use powershell wihout vba.
So far i have this :
ConvertTo-Csv "C:\Users\Me\TestsShella\test.xlsx" | Out-File Q:\test\testShella.csv
But it doesn't working.
With Excel present on the running machine use it as a COM-object:
## Q:\Test\2019\01\31\SO_54461362.ps1
$InFile = Get-Item "$($Env:USERPROFILE)\TestsShella\test.xlsx"
$OutFile= $InFile.FullName.replace($InFile.Extension,".csv")
$Excel = new-object -ComObject "Excel.Application"
$Excel.DisplayAlerts = $True
$Excel.Visible = $False # $True while testing
$WorkBook = $Excel.Workbooks.Open($InFile.FullName)
$WorkBook.SaveAs($OutFile, 6) # 6 -> type csv
$WorkBook.Close($True)
$Excel.Quit()
[void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
Depending on the locale (decimal point/comma) the csv file will either be comma or semicolon seperated.
Without Excel being installed, use the already suggest module ImportExcel
$InFile = Get-Item "$($Env:USERPROFILE)\TestsShella\test.xlsx"
$OutFile= $InFile.FullName.replace($InFile.Extension,".csv")
Import-Excel $Infile.FullName | Export-Csv $OutFile -NoTypeInformation
This yields a .csv file with all fields double quoted and comma seperated.
There is a prebuilt library for this:
https://www.powershellgallery.com/packages/ImportExcel/5.4.4
You will then have the import-excel function/cmdlet available to you and will be able to import, convert to csv and then export
Maybe this could work:
rename-item -Path "C:\Users\Me\TestsShella\test.xlsx" -NewName "item.csv"
you will get a message when open the CSV, but the format of CSV is like XLSX.

Powershell Mass Rename files with a excel reference list

I need help with PowerShell.
I will have to start renaming files in a weekly basis which I will be renaming more than 100 a week or more each with a dynamic name.
The files I want to rename are in a folder name Scans located in the "C: Documents\Scans". And they would be in order, to say time scanned.
I have an excel file located in "C: Documents\Mapping\ New File Name.xlsx.
The workbook has only one sheet and the new names would be in column A with x rows. Like mention above each cell will have different variables.
P Lease make comments on your suggestions so that I may understand what is going on since I'm a new to coding.
Thank you all for your time and help.
Although I agree with Ad Kasenally that it would be easier to use CSV files, here's something that may work for you.
$excelFile = 'C:\Documents\Mapping\New File Name.xlsx'
$scansFolder = 'C:\Documents\Scans'
########################################################
# step 1: get the new filenames from the first column in
# the Excel spreadsheet into an array '$newNames'
########################################################
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$workbook = $excel.Workbooks.Open($excelFile)
$worksheet = $workbook.Worksheets.Item(1)
$newNames = #()
$i = 1
while ($worksheet.Cells.Item($i, 1).Value() -ne $null) {
$newNames += $worksheet.Cells.Item($i, 1).Value()
$i++
}
$excel.Quit
# IMPORTANT: clean-up used Com objects
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($worksheet) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
########################################################
# step 2: rename the 'scan' files
########################################################
$maxItems = $newNames.Count
if ($maxItems) {
$i = 0
Get-ChildItem -Path $scansFolder -File -Filter 'scan*' | # get a list of FileInfo objects in the folder
Sort-Object { [int]($_.BaseName -replace '\D+', '') } | # sort by the numeric part of the filename
Select-Object -First ($maxItems) | # select no more that there are items in the $newNames array
ForEach-Object {
try {
Rename-Item -Path $_.FullName -NewName $newNames[$i] -ErrorAction Stop
Write-Host "File '$($_.Name)' renamed to '$($newNames[$i])'"
$i++
}
catch {
throw
}
}
}
else {
Write-Warning "Could not get any new filenames from the $excelFile file.."
}
You may want to have 2 columns in the excel file:
original file name
target file name
From there you can save the file as a csv.
Use Import-Csv to pull the data into Powershell and a ForEach loop to cycle through each row with a command like move $item.original $item.target.
There are abundant threads describing using import-csv with forEach.
Good luck.