Rename XSL without save prompt using PowerShell - excel

I am a novice to PowerShell and have been working on the following script to look through a directory for XLS and XLSX files. Afterwards, it would get the creation date of each file and rename the filename with the creation date appended to the end.
This script works fine for XLSX files. However when XLS files are encountered, the is save prompt: "Want to save your changes to xxx.xls?"
How can I get rid of this save prompt. Below is my code. Thank you:
Param(
$path = "C:\Excel",
[array]$include = #("*.xlsx","*.xls")
)
$application = New-Object -ComObject Excel.Application
$application.Visible = $false
$binding = "System.Reflection.BindingFlags" -as [type]
[ref]$SaveOption = "microsoft.office.interop.Excel.WdSaveOptions" -as [type]
## Get documents
$docs = Get-childitem -path $Path -Recurse -Include $include
foreach($doc in $docs)
{
try
{
## Get document properties:
$document = $application.Workbooks.Open($doc.fullname)
$BuiltinProperties = $document.BuiltInDocumentProperties
$pn = [System.__ComObject].invokemember("item",$binding::GetProperty,$null,$BuiltinProperties,"Creation Date")
$value = [System.__ComObject].invokemember("value",$binding::GetProperty,$null,$pn,$null)
## Clean up
$document.close([ref]$saveOption::wdDoNotSaveChanges)
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($BuiltinProperties) | Out-Null
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($document) | Out-Null
Remove-Variable -Name document, BuiltinProperties
## Rename document:
$date=$value.ToString('yyyyMMdd');
$strippedFileName = $doc.BaseName;
$extension = $doc.Extension;
#write-host $strippedFileName;
$newName = "$strippedFileName" +"_" + "$date"+ "$extension";
write-host $newName;
Rename-Item $doc $newName
}
catch
{
write-host "Rename failed."
$_
}
}
$application.quit()
$application.Workbooks.Close()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($application) | Out-Null

According to this old kb article, you can trick excel into not prompting you by setting the Saved property on the workbook to true, so I would try:
$document.Saved = $true
$document.close([ref]$saveOption::wdDoNotSaveChanges)

Related

Powershell - Creating Excel Workbook - Getting "Insufficient memory to continue the execution of the program"

I'm trying to create an Excel workbook, then populate the cells with data found from searching many txt files.
I read a file and extract all comments AFTER I find "IDENTIFICATION DIVISION" and BEFORE I find "ENVIRONMENT DIVISION"
I then populate two cells in my excel workbook. cell one if the file and cell two is the comments extracted.
I have 256GB of memory on the work server. less than %5 is being used before Powershell throws the memory error.
Can anyone see where I'm going wrong?
Thanks,
-Ron
$excel = New-Object -ComObject excel.application
$excel.visible = $False
$workbook = $excel.Workbooks.Add()
$diskSpacewksht= $workbook.Worksheets.Item(1)
$diskSpacewksht.Name = "XXXXX_Desc"
$col1=1
$diskSpacewksht.Cells.Item(1,1) = 'Program'
$diskSpacewksht.Cells.Item(1,2) = 'Description'
$CBLFileList = Get-ChildItem -Path 'C:\XXXXX\XXXXX' -Filter '*.cbl' -File -Recurse
$Flowerbox = #()
ForEach($CBLFile in $CBLFileList) {
$treat = $false
Write-Host "Processing ... $CBLFile" -foregroundcolor green
Get-content -Path $CBLFile.FullName |
ForEach-Object {
if ($_ -match 'IDENTIFICATION DIVISION') {
# Write-Host "Match IDENTIFICATION DIVISION" -foregroundcolor green
$treat = $true
}
if ($_ -match 'ENVIRONMENT DIVISION') {
# Write-Host "Match ENVIRONMENT DIVISION" -foregroundcolor green
$col1++
$diskSpacewksht.Cells.Item($col1,1) = $CBLFile.Name
$diskSpacewksht.Cells.Item($col1,2) = [String]$Flowerbox
$Flowerbox = #()
continue
}
if ($treat) {
if ($_ -match '\*(.{62})') {
Foreach-Object {$Flowerbox += $matches[1] + "`r`n"}
$treat = $false
}
}
}
}
$excel.DisplayAlerts = 'False'
$ext=".xlsx"
$path="C:\Desc.txt"
$workbook.SaveAs($path)
$workbook.Close
$excel.DisplayAlerts = 'False'
$excel.Quit()
Not knowing what the contents of the .CBL files could be, I would suggest not to try and do all of this using an Excel COM object, but create a CSV file instead to make things a lot easier.
When finished, you can simply open that csv file in Excel.
# create a List object to collect the 'flowerbox' strings in
$Flowerbox = [System.Collections.Generic.List[string]]::new()
$treat = $false
# get a list of the .cbl files and loop through. Collect all output in variable $result
$CBLFileList = Get-ChildItem -Path 'C:\XXXXX\XXXXX' -Filter '*.cbl' -File -Recurse
$result = foreach ($CBLFile in $CBLFileList) {
Write-Host "Processing ... $($CBLFile.FullName)" -ForegroundColor Green
# using switch -File is an extremely fast way of testing a file line by line.
# instead of '-Regex' you can also do '-WildCard', but then add asterikses around the strings
switch -Regex -File $CBLFile.FullName {
'IDENTIFICATION DIVISION' {
# start collecting Flowerbox lines from here
$treat = $true
}
'ENVIRONMENT DIVISION' {
# stop colecting Flowerbox lines and output what we already have
# output an object with the two properties you need
[PsCustomObject]#{
Program = $CBLFile.Name # or $CBLFile.FullName
Description = $Flowerbox -join [environment]::NewLine
}
$Flowerbox.Clear() # empty the list for the next run
$treat = $false
}
default {
# as I have no idea what these lines may look like, I have to
# assume your regex '\*(.{62})' is correct..
if ($treat -and ($_ -match '\*(.{62})')) {
$Flowerbox.Add($Matches[1])
}
}
}
}
# now you have everything in an array of PSObjects so you can save that as Csv
$result | Export-Csv -Path 'C:\Desc.csv' -UseCulture -NoTypeInformation
Parameter -UseCulture ensures you can double-click the file so it will open correctly in your Excel
You can also create an Excel file from this csv programmatically like:
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$workbook = $excel.Workbooks.Open('C:\Desc.csv')
$worksheet = $workbook.Worksheets.Item(1)
$worksheet.Name = "XXXXX_Desc"
# save as .xlsx
# 51 ==> [Microsoft.Office.Interop.Excel.XlFileFormat]::xlWorkbookDefault
# see: https://learn.microsoft.com/en-us/office/vba/api/excel.xlfileformat
$workbook.SaveAs('C:\Desc.xlsx', 51)
# quit Excel and remove all used COM objects from memory
$excel.Quit()
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($worksheet)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

Converting an Excel file into CSV file in PowerShell

I tried this code as follows:
param(
$inputPath = "N:\Disease_Prevention\....\Samplename.xlsx",
$outputPath = "N:\Disease_Prevention\...\Output.csv"
)
$xlCSV=6
$inputPath = (Resolve-path $inputPath).Path
$outputpath = (Resolve-path $outputpath).Path
get-childitem $inputPath -File | foreach {
write-host "processing $_ "
$Excelfilename = $_.fullname
if(!$outputPath)
{
$outputPath = $_.DirectoryName
}
$CSVfilename =join-path $outputpath $_.BaseName
$CSVfilename+=".csv";
#open excel and save
$Excel = New-Object -comobject Excel.Application
$Excel.Visible = $False
$Excel.displayalerts=$False
$Workbook = $Excel.Workbooks.Open($ExcelFileName)
$Workbook.SaveAs($CSVfilename,$xlCSV)
$Excel.Quit()
If(ps excel){
kill -name excel
}
}
And received the following error as follows:
The error says "Unable to get the SaveAs property of the Workbook class"
Would you know why it could be?
And if it's a permission issue, then would you recommend an alternative way to convert excel into csv? Should I use cmdlet to read each line at the excel file and convert one by one?
The issues I encountered with your code are
$outputpath = (Resolve-path $outputpath).Path
Presumably this CSV file doesn't exist, so when you try to resolve the path it gives you nothing.
if(!$outputPath)
{
$outputPath = $_.DirectoryName
}
$CSVfilename =join-path $outputpath $_.BaseName
$CSVfilename+=".csv";
If you don't pass in an outputpath argument, then this will build the file properly. However, if you pass in an outputpath as a csvfile, then you build out a path that looks like
"N:\Disease_Prevention\...\Output.csv\inputpath basename.csv"
as you're appending the current file's basename and .csv to an already full path.
The adjusted code below worked fine.
param(
$inputPath = "N:\Disease_Prevention\....\Samplename.xlsx",
$outputPath = "N:\Disease_Prevention\...\Output.csv"
)
$xlCSV=6
$inputPath = (Resolve-path $inputPath).Path
get-childitem $inputPath -File | foreach {
write-host "processing $_ "
$Excelfilename = $_.fullname
if(!$outputPath)
{
$outputPath = $_.DirectoryName
$CSVfilename = join-path $outputpath $_.BaseName
$CSVfilename+=".csv"
}
else{
$CSVfilename = $outputPath
}
#open excel and save
$Excel = New-Object -comobject Excel.Application
$Excel.Visible = $False
$Excel.displayalerts=$False
$Workbook = $Excel.Workbooks.Open($ExcelFileName)
$Workbook.SaveAs($CSVfilename,$xlCSV)
$Excel.Quit()
If(ps excel){
kill -name excel
}
}
I also encourage you to check out Doug Finke's ImportExcel powershell module. Makes working with Excel files so much easier and it doesn't require Excel be installed on the machine that runs the script.

Rename multiple worksheet and multiple files with powershell

Sorry for my bad english I'm French.
I'm new at Powershell and I wanted to rename all of my worksheet in multiple files.
So far i got that code that rename all files in a directory
Function Rename()
{
$path = Get-Location
$files = Get-ChildItem
$counter = 1
foreach($file in $files)
{
Rename-Item $file.FullName "$counter" + ".xlsx"
$counter++
}
}
Then I tried on a specific file to rename all of the sheets that are inside but it doesn't work. The loop works only one time while it has 4 worksheets.
Function RenameTab ($ExcelFileName)
{
#Emplacement du fichier
$excelFile = "C:\Users\Donosaure\Documents\Magister\" + $excelFileName + ".xlsx"
#Ouverture d'excel
$xldoc = New-Object -ComObject "Excel.Application"
#Message de confirmation
$xldoc.Visible = $false
$xldoc.DisplayAlerts = $false
#Ouverture du fichier
$workbook = $xldoc.Workbooks.Open($excelFile)
$inc = 1
$i=1
foreach ($worksheet in $workbook.Worksheets.count)
{
$worksheet = $workbook.Sheets.Item($i)
$worksheet.Name = $inc
$inc++
$i++
$workbook.SaveAs("C:\Users\Donosaure\Documents\Magister\1.xlsx")
$workbook.Close()
}
$xldoc.Quit()
}
RenameTab("Magister")
Can somebody help me ?
Thanks for your time
A few remarks about your code:
Parameters sent to a function in PowerShell are separated by space, you should not use brackets around them as in RenameTab("Magister")
When using COM objects, always make sure you release them from memory when done, otherwise they will linger in memory and if you run this again and again, you will run out of resources otherwise
Please check for Excel Worksheets Naming Convention, so you do not create worksheet names with invalid characters. At the moment, that is not the case, but you never know how this evolves.
Please use the PowerShell function 'Verb-Noun' naming convention for functions you create.
Below should do what you want:
Function Rename-ExcelTabs ($ExcelFileName) {
#Emplacement du fichier
$excelFile = "C:\Users\Donosaure\Documents\Magister\" + $excelFileName + ".xlsx"
#Ouverture d'excel
$xldoc = New-Object -ComObject "Excel.Application"
#Message de confirmation
$xldoc.Visible = $false
$xldoc.DisplayAlerts = $false
#Ouverture du fichier
$workbook = $xldoc.Workbooks.Open($excelFile)
for ($i = 1; $i -le $workbook.Worksheets.Count; $i++) {
$workbook.Sheets.Item($i).Name = $i
}
$workbook.SaveAs("C:\Users\Donosaure\Documents\Magister\" + $excelFileName + "_1.xlsx")
$workbook.Close()
$xldoc.Quit()
# clean-up used COM objects
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($xldoc)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
Rename-ExcelTabs "Magister"
As per your comment, the function could be rewritten to not only change the tab names in one Excel file, but process all .xlsx files inside a folder and rename these files aswell.
One way would be do remove the original file after the tabs have been renamed and a new file is created with $workbook.SaveAs(), as in the code above.
The following function does this by renaming the file first and next change the tab names in it.
function Rename-ExcelTabsAndFiles {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[ValidateScript({Test-Path -Path $_ -PathType Container})]
[Alias('Path')]
[string]$SourceFolder
)
# get a list of xlsx files
$allFiles = Get-ChildItem -Path $SourceFolder -Filter '*.xlsx' -File
# create an Excel object
$xldoc = New-Object -ComObject Excel.Application
# Message de confirmation
$xldoc.Visible = $false
$xldoc.DisplayAlerts = $false
$fileCount = 1
foreach ($excelFile in $allFiles) {
# rename the file. use -PassThru to get the FileInfo object of the renamed file
# apparently you want the files to be called '1.xlsx', '2.xlsx' etc.
$newName = '{0}.xlsx' -f $fileCount++
Write-Host "Renaming file '$($excelFile.Name)' to '$newName'"
$excelFile = Rename-Item -Path $excelFile.FullName -NewName $newName -PassThru
# Ouverture du fichier
$workbook = $xldoc.Workbooks.Open($excelFile.FullName)
# rename all worksheets in the file
Write-Host "Renaming all worksheets in '$newName'"
for ($i = 1; $i -le $workbook.Worksheets.Count; $i++) {
$workbook.Sheets.Item($i).Name = $i
}
$workbook.Save()
$workbook.Close()
}
$xldoc.Quit()
# clean-up used COM objects
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($xldoc)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
Rename-ExcelTabsAndFiles "C:\Users\Donosaure\Documents\Magister"

Add Version Info to Excel file properties with VBA, read with PowerShell

My goal is to add a version number to the file properties of an Excel file that can then be read externally with PowerShell.
If I run (Get-Item "example.xls").VersionInfo I get blank ProductVersion and FileVersion.
ProductVersion FileVersion FileName
-------------- ----------- --------
example.xls
I cannot find a way to set these attributes from VBA. I did find a way to get\set a Revision Number:
Public Function FileVersion() As String
With ThisWorkbook.BuiltinDocumentProperties
FileVersion = .Item("Revision Number").Value
End With
End Function
Public Sub UpdateFileVersion()
With ThisWorkbook.BuiltinDocumentProperties
.Item("Revision Number").Value = .Item("Revision Number").Value + 1
End With
End Sub
However, I can't find a way to read the Revision Number from PowerShell. I either need to read Revision Number from PowerShell or I need to set ProductVersion and FileVersion from VBA. I would accept any combination of things that results in setting a file version in Excel that is visible outside of Excel, ideally I would like to be able to use all of these properties.
You can see the Revision Number I am trying to get from PowerShell and also the Version Number that I cannot set from VBA here:
If you right-click a file and hit properties in the Details tab, you see all that is available.
If you don't want to have to COM into the EOM (Excel Object Model), then you need to assign these in the EOM first, then hit them via PowerShell just as Windows Explorer shows them or enum metadata.
So, something like...
### Get file properties
##
Get-ItemProperty -Path 'D:\Temp' -filter '*.xl*' |
Format-list -Property * -Force
Or
### Enumerate file properties in PowerShell
# get the first file
(
$Path = ($FileName = (Get-ChildItem -Path 'D:\Temp' -Filter '*.xl*').FullName ) |
Select-Object -First 1
)
$shell = New-Object -COMObject Shell.Application
$folder = Split-Path $path
$file = Split-Path $path -Leaf
$shellfolder = $shell.Namespace($folder)
($shellfile = $shellfolder.ParseName($file))
<#
You'll need to know what the ID of the extended attribute is.
This will show you all of the ID's:
#>
0..287 |
Foreach-Object { '{0} = {1}' -f $_, $shellfolder.GetDetailsOf($null, $_) }
# Once you find the one you want you can access it like this:
$shellfolder.GetDetailsOf($shellfile, 216)
As for this...
Thanks but your list, and the one I got from running this on my Excel
file, do not contain Revision
... try it this way.
Gleened from here:
Hey, Scripting Guy! How Can I List All the Properties of a Microsoft
Word Document?
and here:
# Getting specific properties fomr MS Word
$Path = "D:\Temp"
$ObjectProperties = "Author","Keywords","Revision number"
$Application = New-Object -ComObject Word.Application
$Application.Visible = $false
$Binding = "System.Reflection.BindingFlags" -as [type]
$Select = "Name","Created"
$Select += $ObjectProperties
ForEach ($File in (Get-ChildItem $Path -Include '*.docx' -Recurse))
{ $Document = $Application.Documents.Open($File.Fullname)
$Properties = $Document.BuiltInDocumentProperties
$Hash = #{}
$Hash.Add("Name",$File.FullName)
$Hash.Add("Created",$File.CreationTime)
ForEach ($Property in $ObjectProperties)
{ $DocProperties = [System.__ComObject].InvokeMember("item",$Binding::GetProperty,$null,$Properties,$Property)
Try {$Value = [System.__ComObject].InvokeMember("value",$binding::GetProperty,$null,$DocProperties,$null)}
Catch {$Value = $null}
$Hash.Add($Property,$Value)
}
$Document.Close()
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($Properties) |
Out-Null
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($Document) |
Out-Null
New-Object PSObject -Property $Hash |
Select $Select
}
$Application.Quit()
# Results
<#
Name : D:\Temp\Test.docx
Created : 06-Feb-20 14:23:55
Author : ...
Keywords :
Revision number : 5
#>
# Getting specific properties fomr MS Excel
$Path = "D:\Temp"
$ObjectProperties = "Author","Keywords","Revision number"
$Application = New-Object -ComObject excel.Application
$Application.Visible = $false
$Binding = "System.Reflection.BindingFlags" -as [type]
$Select = "Name","Created"
$Select += $ObjectProperties
ForEach ($File in (Get-ChildItem $Path -Include '*.xlsx' -Recurse))
{ $Document = $Application.Workbooks.Open($File.Fullname)
$Properties = $Document.BuiltInDocumentProperties
$Hash = #{}
$Hash.Add("Name",$File.FullName)
$Hash.Add("Created",$File.CreationTime)
ForEach ($Property in $ObjectProperties)
{ $DocProperties = [System.__ComObject].InvokeMember("item",$Binding::GetProperty,$null,$Properties,$Property)
Try {$Value = [System.__ComObject].InvokeMember("value",$binding::GetProperty,$null,$DocProperties,$null)}
Catch {$Value = $null}
$Hash.Add($Property,$Value)
}
$Document.Close()
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($Properties) |
Out-Null
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($Document) |
Out-Null
New-Object PSObject -Property $Hash |
Select $Select
}
$Application.Quit()
# Results
<#
Name : D:\Temp\Test.xlsx
Created : 25-Nov-19 20:47:15
Author : ...
Keywords :
Revision number : 2
#>
Point of note: I meant to add sources:
Regarding setting properties, see this Word example from the MS PowerShellgallery.com, which can be tweaked of course for other Office docs.
Set specific word document properties using PowerShell
The attached script uses the Word automation model to set a specific
BuiltIn Word document property. It is provided as an example of how to
do this. You will need to modify the pattern used to find the files,
as well as the built-in Word property and value you wish to assign.
As note above, getting is the same thing...
Get Word built-in document properties
This script will allow you to specify specific Word built-in document
properties. It returns an object containing the specified word
document properties as well as the path to those documents. Because a
PowerShell object returns, you can filter and search different
information fr
Thanks to #postanote for pointing me in the right direction. None of the code offered worked out of the box for me.
This is what I ended up doing to pull the Revision Number from my Excel document:
<# Get-Excel-Property.ps1 v1.0.0 by Adam Kauffman 2020-02-03
Returns the property value from an Excel File
#>
param(
[Parameter(Mandatory=$true, Position=0)][string]$FilePath,
[Parameter(Mandatory=$true, Position=1)][string]$ObjectProperties
)
Function Get-Property-Value {
[CmdletBinding()]Param (
[Parameter(Mandatory = $true)]$ComObject,
[Parameter(Mandatory = $true)][String]$Property
)
$Binding = "System.Reflection.BindingFlags" -as [type]
Try {
$ObjectType = $ComObject.GetType()
$Item = $ObjectType.InvokeMember("Item",$Binding::GetProperty,$null,$ComObject,$Property)
return $ObjectType.InvokeMember("Value",$Binding::GetProperty,$null,$Item,$null)
}
Catch {
return $null
}
}
# Main
$Application = New-Object -ComObject Excel.Application
$Application.Visible = $false
$Document = $Application.Workbooks.Open($FilePath)
$Properties = $Document.BuiltInDocumentProperties
$Hash = #{}
$Hash.Add("Name",$FilePath)
ForEach ($Property in $ObjectProperties)
{
$Value = Get-Property-Value -ComObject $Properties -Property $Property
$Hash.Add($Property,$Value)
}
# COM Object Cleanup
if ($null -ne $Document) {
$Document.Close($false)
Remove-Variable -Name Document
}
if ($null -ne $Properties) {
Remove-Variable -Name Properties
}
if ($null -ne $Application) {
$Application.Quit()
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($Application) | Out-Null
Remove-Variable -Name Application
}
[gc]::collect()
[gc]::WaitForPendingFinalizers()
# Show collected information
New-Object PSObject -Property $Hash

Merging CSV Files into a XLSX with Tabs

I currently have 5 Registry CSV files which are created during a PowerShell script:
HKCC
HKCR
HKCU
HKLM
HKU
I need these CSV files to open at the end of the script however would like if all of them were contained within one XLSX file with 5 different headings
Is there a way to combine the files through PowerShell?
I understand how to get the data of the CSV files but don't understand how to merge them or convert. Some of the variables I believe which may be helpful.
$Date = Get-Date -Format "d.MMM.yyyy"
$DIR = $WPFlistview.Selecteditem.Ransomware
$path = "F:\Registry_Export\Results\$DIR\$Date\*"
$csvs = Get-ChildItem $path -Include *.csv
$output = "F:\Registry_Export\Results\$DIR\$Date\Results.Xlsx"
Paths to the CSV files if needed:
F:\Registry_Export\Results\$DIR\$Date\HKCR.CSV
F:\Registry_Export\Results\$DIR\$Date\HKCU.CSV
F:\Registry_Export\Results\$DIR\$Date\HKLM.CSV
F:\Registry_Export\Results\$DIR\$Date\HKU.CSV
F:\Registry_Export\Results\$DIR\$Date\HKCC.CSV
This is what I have tried prior. However, it completly scrambles my data into the wrong lines and cells:
function MergeCSV {
$Date = Get-Date -Format "d.MMM.yyyy"
$DIR = $WPFlistview.Selecteditem.Ransomware
$path = "F:\Registry_Export\Results\$DIR\$Date\*"
$csvs = Get-ChildItem $path -Include *.csv
$y = $csvs.Count
Write-Host "Detected the following CSV files: ($y)"
foreach ($csv in $csvs) {
Write-Host " "$csv.Name
}
$outputfilename = "Final Registry Results"
Write-Host Creating: $outputfilename
$excelapp = New-Object -ComObject Excel.Application
$excelapp.SheetsInNewWorkbook = $csvs.Count
$xlsx = $excelapp.Workbooks.Add()
$sheet = 1
foreach ($csv in $csvs) {
$row = 1
$column = 1
$worksheet = $xlsx.Worksheets.Item($sheet)
$worksheet.Name = $csv.Name
$file = (Get-Content $csv)
foreach ($line in $file) {
$linecontents = $line -split ',(?!\s*\w+")'
foreach ($cell in $linecontents) {
$worksheet.Cells.Item($row,$column) = $cell
$column++
}
$column = 1
$row++
}
$sheet++
}
$output = "F:\Registry_Export\Results\$DIR\$Date\Results.Xlsx"
$xlsx.SaveAs($output)
$excelapp.Quit()
}
How the CSV looks
https://gyazo.com/177c7c3bb21ddf06d0ebacbb7f4d537b
How the XLSX looks
https://gyazo.com/cd5fb48d61f93aac5ec3034d81811094
So, using the Excel.Application ComObject still, what I would suggest is loading each CSV as a CSV, not using Get-Content like you are. Then use the ConvertTo-CSV cmdlet, specifying to use tab as the delimiter, and copy that to the clipboard. Then just paste into Excel, and it will paste in fairly nicely. You may want to adjust column size, but the data will show up just as you would expect it to. I would also use a For loop instead of a ForEach loop, since Excel plays nice with numbers for the tabs (though it is 1 based instead of PowerShell's 0 base). Here's what I would end up with after making those modifications:
function MergeCSV {
$Date = Get-Date -Format "d.MMM.yyyy"
$DIR = $WPFlistview.Selecteditem.Ransomware
$path = "F:\Registry_Export\Results\$DIR\$Date\*"
$csvs = Get-ChildItem $path -Include *.csv
$y = $csvs.Count
Write-Host "Detected the following CSV files: ($y)"
Write-Host " "$csvs.Name"`n"
$outputfilename = "Final Registry Results"
Write-Host Creating: $outputfilename
$excelapp = New-Object -ComObject Excel.Application
$excelapp.SheetsInNewWorkbook = $csvs.Count
$xlsx = $excelapp.Workbooks.Add()
for($i=1;$i -le $y;$i++) {
$worksheet = $xlsx.Worksheets.Item($i)
$worksheet.Name = $csvs[$i-1].Name
$file = (Import-Csv $csvs[$i-1].FullName)
$file | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Clip
$worksheet.Cells.Item(1).PasteSpecial()|out-null
}
$output = "F:\Registry_Export\Results\$DIR\$Date\Results.Xlsx"
$xlsx.SaveAs($output)
$excelapp.Quit()
}
You could use ImportExcel by Doug Finke And then replace your Export-CSV in the original script with Export-Excel -WorksheetName
Install-Module ImportExcel
Export-Excel "F:\Registry_Export\Results\$DIR\$Date\Results.xlsx" -worksheetname "HKCR"
Export-Excel "F:\Registry_Export\Results\$DIR\$Date\Results.xlsx" -worksheetname "HKCU"

Resources