Split excel sheets without cell formula (links) using PowerShell - excel

I have excel with multiple sheets and I'm trying to split sheets into separate workbooks. Excel sheets are linked between, so when I split them cells will have linked values. Do you have idea how to split excel sheets but paste only values, without formulas?
$Excel = New-Object -ComObject "Excel.Application"
$Excel.Visible = $false
$Excel.DisplayAlerts = $false
$filepath ="C:\Users\XX\Documents\2020\XXX_test.xlsx"
$Workbook = $Excel.Workbooks.open($filepath)
$WorkbookName = "test.xlsx"
$output_type = "xlsx"
if ($Workbook.Worksheets.Count -gt 0) {
write-Output "Now processing: $WorkbookName"
$FileFormat = [Microsoft.Office.Interop.Excel.XlFileFormat]::xlOpenXMLWorkbook
$WorkbookName = $filepath -replace ".xlsx", ""
foreach($Worksheet in $Workbook.Worksheets) {
$Worksheet.Copy()
$ExtractedFileName = $WorkbookName + "~~" + $Worksheet.Name + "." + $output_type
$Excel.ActiveWorkbook.SaveAs($ExtractedFileName, $FileFormat)
$Excel.ActiveWorkbook.Close
write-Output "Created file: $ExtractedFileName"
}
}
$Workbook.Close()
$Excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
Stop-Process -Name EXCEL
Remove-Variable Excel

The ImportExcel module makes this easier.
Import-Module ImportExcel
$filepath ="C:\Users\XX\Documents\2020\XXX_test.xlsx"
$output_type = "xlsx"
$sheets = Get-ExcelSheetInfo -Path $filepath
foreach ($sheet in $sheets) {
$ExtractedFileName = "{0}~~{1}.{2}" -f ($filepath -replace '\.xlsx$'),$sheet.Name,$output_type
Import-Excel -Path $sheet.Path -WorkSheetName $sheet.Name |
Export-Excel -Path $ExtractedFileName
}

Related

Powershell Excel SaveAs requires confirmation

I use below script to convert bunch of xls files to xlsx.
$folderpath = %tempPath%
$filetype ="*xls"
Add-Type -AssemblyName Microsoft.Office.Interop.Excel
$xlFixedFormat = [Microsoft.Office.Interop.Excel.XlFileFormat]::xlWorkbookDefault
write-host $xlFixedFormat
$excel = New-Object -ComObject excel.application
$excel.visible = $true
Get-ChildItem -Path $folderpath -Include $filetype -recurse |
ForEach-Object `
{
$path = ($_.fullname).substring(0, ($_.FullName).lastindexOf("."))
"Converting $path"
$workbook = $excel.workbooks.open($_.fullname)
$path += ".xlsx"
$workbook.saveas($path, $xlFixedFormat)
$workbook.close()
}
$excel.Quit()
$excel = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()
It used to work perfectly running on VM.
Unfortunately with changing folder path I realised there are popup windows to confirm saving that didn't come up before and the script gets stuck on that.
Any simple corrections that could prevent that error?
"scriptError": {
"localizedName": "Error",
"value": "Unable to get the SaveAs property of the Workbook class\r\nAt C:\\Users\\~
"variableName": "ScriptError"
}
Here's an example of how I set the path when saving an Excel file using PowerShell. I set the path using a combination of the Get-Location cmdlet, Get-Date cmdlet and the file name, which is stored in a string variable for use when saving the script.
Add-Type -AssemblyName Microsoft.Office.Interop.Excel
$xlFixedFormat = [Microsoft.Office.Interop.Excel.XlFileFormat]::xlWorkbookDefault
$htFixedFormat = [Microsoft.Office.Interop.Excel.XlFileFormat]::xlHtml
$Date = get-date -format R
$CurrentLocation = Get-Location
$CurrentDir = Get-location
$Timestamp = get-date -format d
$xlsx = [String] $CurrentLocation + "\MyNewExcelStuff-" + $Timestamp + ".xlsx"
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$excel.DisplayAlerts = $False
$workbook = $excel.Workbooks.add()
$sheet1 = $workbook.worksheets.Item(1)
$sheet1.name = "Stuff"
$Sheet1.Cells.Item(1,1) = "Reporting Stack Stuff"
$title = $Sheet1.Range("A1:K1")
$title.Select()
$title.MergeCells = $true
$title.VerticalAlignment = -4108 # Centre (vertically) heading
$title.HorizontalAlignment = -4108 # Centre (horizontally) heading
$Title.Interior.ColorIndex = 0
$Excel.ActiveWorkbook.SaveAs($xlsx, $xlFixedFormat)
Start-Sleep -s 2
$Excel.Quit()
$Excel = $Null
You should use $workbook.Close($false).

How to use powershell to select range and dump that to csv file

Actually, this is a version of question here:
How to use powershell to select and copy columns and rows in which data is present in new workbook.
The goal is to grab certain columns from multiple Excel workbooks and dump everything to one csv file. Columns are always the same.
I'm doing that manually:
$xl = New-Object -ComObject Excel.Application
$xl.Visible = $false
$xl.DisplayAlerts = $false
$counter = 0
$input_folder = "C:\Users\user\Documents\excelfiles"
$output_folder = "C:\Users\user\Documents\csvdump"
Get-ChildItem $input_folder -File |
Foreach-Object {
$counter++
$wb = $xl.Workbooks.Open($_.FullName, 0, 1, 5, "")
try {
$ws = $wb.Worksheets.item('Calls') # => This specific worksheet
$rowMax = ($ws.UsedRange.Rows).count
for ($i=1; $i -le $rowMax-1; $i++) {
$newRow = New-Object -Type PSObject -Property #{
'Type' = $ws.Cells.Item(1+$i,1).text
'Direction' = $ws.Cells.Item(1+$i,2).text
'From' = $ws.Cells.Item(1+$i,3).text
'To' = $ws.Cells.Item(1+$i,4).text
}
$newRow | Export-Csv -Path $("$output_folder\$ESO_Output") -Append -noType -Force
}
}
} catch {
Write-host "No such workbook" -ForegroundColor Red
# Return
}
}
Question:
This works, but is extremely slow because Excel has to select every cell, copy that, then Powershell has to create array and save row by row in output csv file.
Is there a method to select a range in Excel (number of columns times ($ws.UsedRange.Rows).count), cut header line and just append this range (array?) to csv file to make everything much faster?
So that's the final solution
Script is 22 times faster!!! than original solution.
Hope somebody will find that useful :)
PasteSpecial is to filter out empty rows. There is no need to save them into csv
$xl = New-Object -ComObject Excel.Application
$xl.Visible = $false
$xl.DisplayAlerts = $false
$counter = 0
$input_folder = "C:\Users\user\Documents\excelfiles"
$output_folder = "C:\Users\user\Documents\csvdump"
Get-ChildItem $input_folder -File |
Foreach-Object {
$counter++
try {
$new_ws1 = $wb.Worksheets.add()
$ws = $wb.Worksheets.item('Calls')
$rowMax = ($ws.UsedRange.Rows).count
$range = $ws.Range("A1:O$rowMax")
$x = $range.copy()
$y = $new_ws1.Range("A1:O$rowMax").PasteSpecial([System.Type]::Missing,[System.Type]::Missing,$true,$false)
$wb.SaveAs("$($output_folder)\$($_.Basename)",[Microsoft.Office.Interop.Excel.XlFileFormat]::xlCSVWindows)
} catch {
Write-host "No such workbook" -ForegroundColor Red
# Return
}
}
$xl.Quit()
Part above will generate a bunch of csv files.
Part below will read these files in separate loop and combine them together into one.
-exclude is an array of something I want to omit
Remove-Item to remove temporary files
Answer below is based on this post: https://stackoverflow.com/a/27893253/6190661
$getFirstLine = $true
Get-ChildItem "$output_folder\*.csv" -exclude $excluded | foreach {
$filePath = $_
$lines = Get-Content $filePath
$linesToWrite = switch($getFirstLine) {
$true {$lines}
$false {$lines | Select -Skip 1}
}
$getFirstLine = $false
Add-Content "$($output_folder)\MERGED_CSV_FILE.csv" $linesToWrite
Remove-Item $_.FullName
}

Why is my code not saving the correct worksheet?

I have this following Powershell script to convert the 2nd sheet on a XLSB file to CSV.
Function XLSBtoCSV ($Path)
{
foreach($File in (Get-childItem $Path -Filter "*.xlsb"))
{
$pwd = $Path
$excelFile = "$pwd\" + $File
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $false
$Excel.DisplayAlerts = $false
$wb = $Excel.Workbooks.Open($excelFile)
$ws = $wb.Worksheets.item(2)
$ws.SaveAs("$pwd\" + $File.BaseName + "-" + $ws.name + ".csv", 6)
$Excel.Quit()
}
}
$FilePath = Get-Location
XLSBtoCSV -Path $FilePath
This script used to work but somehow now it only saves the last worksheet (sheet 3). I have tried to change to different sheet number but every time, the last worksheet is saved.
Very close. You need to loop through the worksheet items instead of just calling items(2) as covered in another answer:
Function XLSBtoCSV ($Path)
{
foreach($File in (Get-childItem $Path -Filter "*.xlsb"))
{
$pwd = $Path
$excelFile = "$pwd\" + $File
try{
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $false
$Excel.DisplayAlerts = $false
$wb = $Excel.Workbooks.Open($excelFile)
# source https://stackoverflow.com/questions/16156951/how-to-iterate-through-excel-worksheets-only-extracting-data-from-specific-colum
foreach($ws in $wb.Worksheets)
{
$ws.SaveAs("$pwd\" + $File.BaseName + "-" + $ws.name + ".csv", 6)
}
}
finally
{
# close/dispose all the open parts of Excel
if($Excel)
{
$Excel.Quit()
}
}
}
}
$FilePath = Get-Location
XLSBtoCSV -Path $FilePath

Powershell with Excel

What I need to do is to extract the data in the excel row and output them into different rows on Excel. After that, I will need to use the extracted data and perform certain conditions on the extracted data.
This is my current script:
To open excel and apply the formulas
$excel = New-Object -ComObject excel.application
$filepath = 'D:\testexcel.xlsx'
$workbook = $excel.workbooks.open("$filepath")
$worksheet = $workbook.worksheets.item(1)
$excel.Visible = $true
$rows = $worksheet.range("A1").currentregion.rows.count
$worksheet.range("S1:S$rows").formula = $worksheet.range("S1").formula
Function to find the row, apply the formula and output it
function test123(){
param([string]$test123)
$sourcefile = "D:\testexcel.xlsx"
$sheetname = "abc"
$excel = new-object -comobject excel.application
$excel.Visible = $true
$excelworkbook = $excel.Workbooks.open($sourcefile, 2, $true)
$excelworksheet = $excelworkbook.worksheets.item($sheetname)
$row = 1
$column = 1
$found = $false
while(($excelworksheet.cells.item($row, $column).value() -ne $null) -and($found -eq $false)){
if(($excelworksheet.cells.item($row, $column).value()).toupper() -eq $test123.ToUpper()){
write-host $excelworksheet.cells.item($row, $column).value() $excelworksheet.cells.item($row, $column+1).value(),
$excelworksheet.cells.item($row, $column +2).value() $found = $true
}
$row += 1
}
#close workbook
$excelworkbook.close()
$excel.quit()
}
test123 -test123 "Test123"
Please guide me and tell me if this is the right way to do it... Thanks
Please have a look into the ImportExcel module by Douge Finke. This module has the capability to do what you need.
Get it from PowerShell gallery: Install-Module -Name ImportExcel
Github link: https://github.com/dfinke/ImportExcel
you can then do Get-Help Import-Excel -Examples which has pretty good examples.

Can PowerShell generate a plain Excel file with multiple sheets?

The Export-Csv cmdlet can export data as a CSV file, which can then be imported in Excel. However, what I'd like to do is to merge different CSV files (with different column definition) into one Excel file having multiple sheets (where each sheet is different).
Can PowerShell call the Excel DLL directly and construct sheets from CSV files?
You can use Excel.ComObject:
## Excel must be installed for this function to work...
Function Merge-CSVFiles
{
Param(
$CSVPath = "C:\CSV", ## Soruce CSV Folder
$XLOutput="c:\temp.xlsx" ## Output file name
)
$csvFiles = Get-ChildItem ("$CSVPath\*") -Include *.csv
$Excel = New-Object -ComObject Excel.Application
$Excel.visible = $false
$Excel.sheetsInNewWorkbook = $csvFiles.Count
$workbooks = $excel.Workbooks.Add()
$CSVSheet = 1
Foreach ($CSV in $Csvfiles)
{
$worksheets = $workbooks.worksheets
$CSVFullPath = $CSV.FullName
$SheetName = ($CSV.name -split "\.")[0]
$worksheet = $worksheets.Item($CSVSheet)
$worksheet.Name = $SheetName
$TxtConnector = ("TEXT;" + $CSVFullPath)
$CellRef = $worksheet.Range("A1")
$Connector = $worksheet.QueryTables.add($TxtConnector,$CellRef)
$worksheet.QueryTables.item($Connector.name).TextFileCommaDelimiter = $True
$worksheet.QueryTables.item($Connector.name).TextFileParseType = 1
$worksheet.QueryTables.item($Connector.name).Refresh()
$worksheet.QueryTables.item($Connector.name).delete()
$worksheet.UsedRange.EntireColumn.AutoFit()
$CSVSheet++
}
$workbooks.SaveAs($XLOutput,51)
$workbooks.Saved = $true
$workbooks.Close()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbooks) | Out-Null
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
Update the Path for the CSV files, it will create a sheet for each CSV in the folder,
Run it like this:
Merge-CSVFiles -CSVPath C:\CsvFolder -XLOutput C:\ExcelFile.xlsx
Using the Export-excel method will create multiple sheets
$a=[pscustomobject]#{ColumnOne=1;ColumnTwo=2;}
$b=[pscustomobject]#{ColumnOne=1;ColumnTwo=2;}
$a|Export-excel $fileName -Autosize -TableName A -WorksheetName A
$b|Export-excel $fileName -Autosize -TableName B -WorksheetName B
The resulting spreadsheet will look as follows...

Resources