Using Powershell to loop through Excel files and check if Spreadsheet name exists - excel

I'm trying to write a powershell script that will loop through each excel file in the given directory, check the file for a specifically named worksheet, and then copy that file to another location if it's a match.
Please see below for what I've already tried:
[void][reflection.assembly]::Loadwithpartialname("microsoft.office.excel")
$Excel = New-Object -ComObject Excel.Application
$tempLocation = "C:\Test\" # Path to read files
$files = Get-ChildItem C:\Test
ForEach ($file in $files)
{
#Check for Worksheet named TestSheet
$WorkBook = $Excel.Workbooks.Open($file)
$WorkSheets = $WorkBook.WorkSheets
foreach ($WorkSheet in $Workbook.Worksheets) {
If ($WorkSheet.Name -eq "TestSheet")
{$path = $tempLocation + "\" + $file
Write "Saving $path"
Copy-Item c:\Test\$file c:\Confirmed}
Else {Write "$path does not contain TestSheet"}
$WorkBook.Close()
}
}
This script returns no errors in PowerShell, but just sits there without writing anything or copying any files. Any ideas?
EDIT: Here's my final script that is now running successfully
$ErrorActionPreference= 'silentlycontinue'
$tempLocation = "C:\Source" # Path to read files
$targetlocation = "C:\Target"
Write "Loading Files..."
$files = Get-ChildItem C:\Source
Write "Files Loaded."
ForEach ($file in $files)
{
#Check for Worksheet named TestSheet
$Excel = New-Object -ComObject Excel.Application
$Excel.visible = $false
$Excel.DisplayAlerts = $false
$WorkBook = $Excel.Workbooks.Open($file.Fullname)
$WorkSheets = $WorkBook.WorkSheets | where {$_.name -eq "TestSheet"}
if($WorkSheets) {
$path = $tempLocation + "\" + $file
$dest = $targetlocation + "\" + $file
Write "Saving $path"
$WorkBook.SaveAs($dest)
}
$Excel.Quit()
Stop-Process -processname EXCEL
}
Read-host -prompt "The Scan has completed. Press ENTER to close..."
clear-host;

There were several issues with my script's logic. The following script ran successfully! It took hours of research...
$ErrorActionPreference= 'silentlycontinue'
$tempLocation = "C:\Source" # Path to read files
$targetlocation = "C:\Target"
Write "Loading Files..."
$files = Get-ChildItem C:\Source
Write "Files Loaded."
ForEach ($file in $files)
{
#Check for Worksheet named TestSheet
$Excel = New-Object -ComObject Excel.Application
$Excel.visible = $false
$Excel.DisplayAlerts = $false
$WorkBook = $Excel.Workbooks.Open($file.Fullname)
$WorkSheets = $WorkBook.WorkSheets | where {$_.name -eq "TestSheet"}
if($WorkSheets) {
$path = $tempLocation + "\" + $file
$dest = $targetlocation + "\" + $file
Write "Saving $path"
$WorkBook.SaveAs($dest)
}
$Excel.Quit()
Stop-Process -processname EXCEL
}
Read-host -prompt "The Scan has completed. Press ENTER to close..."
clear-host;

You don't need this line:
[void][reflection.assembly]::Loadwithpartialname("microsoft.office.excel")
($Excel = New-Object -ComObject Excel.Application is sufficient here)
I don't think you're referencing the full path to your Excel files. Try modifying this line:
$WorkBook = $Excel.Workbooks.Open($file)
Amend to:
$WorkBook = $Excel.Workbooks.Open($file.Fullname)
Additionally, consider adding a filter to your Get-ChildItem command, if there are sub-directories or non-Excel files, they will cause errors:
$files = Get-ChildItem C:\Test -filter "*.xls"

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).

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.

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

code for bulk CSV files in a directory to convert to xlsx

I used below code to convert file from CSV to xlsx. But it only convert single file at a time. I want this to convert all the files in directory at a time.
$xl = New-Object -ComObject Excel.Application
$xl.Visible = $true
$Workbook = $xl.Workbooks.Open("$loglocation\errors_$server.csv")
$Worksheets = $Workbooks.Worksheets
$Workbook.SaveAs("$loglocation\errors_$server.xls",1)
$Workbook.Saved = $true
$xl.Quit()
With the PSExcel Module you can use Export-XLSX which makes this process very simple:
$loglocation = "C:\folder"
Get-ChildItem -Path $loglocation -Filter *.csv | foreach {
Export-XLSX -InputObject $_ -Path "$loglocation\$($_.BaseName).xlsx"
}
Try this, should work:
$filePath = Get-ChildItem -Path "path to csv" -filter *.csv
foreach ($file in $filePath )
{
$filename = $file.FullName
$filename
$xl = new-object -comobject excel.application
$xl.visible = $true
$Workbook = $xl.workbooks.open($filename)
$Worksheets = $Workbooks.worksheets
$Workbook.SaveAs($filename.Substring(0,$filename.Length-4) + ".xlsx",1)
$Workbook.Saved = $True
$xl.Quit()
}
Can move the excel connections outside the loop as well if you need to speed it up

Creating a powershell script to convert and excel worksheet to pdf

I have done some searching and have gotten close but, still don't have a completely working script. Does anyone know what I'm doing wrong and what I need to change or add for this to work correctly.
Here is the code:
$path = "D:\Geoff's Files"
$dir = "D:\Geoff's Files\*.xls
$xlFixedFormat = Microsft.Office.Interop.Excel.xlFixedFormatType" -as [type]
$latest = Get-ChildItem -Path $dir | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$latest.name
$ExcelFile = "$latest"
$excel = New-Object -ComObject Excel.Application
$excel.visible = $true
$Workbook = $excel.workbooks.open($ExcelFile,2,$True)
$Worksheets = $Workbook.worksheets
$Worksheet = $Workbook.worksheets.Item(1)
$Worksheet.ExportAsFixedFormat($xlFixedFormat::XlTypePDF, $path)
$workbook.Close()
$excel.Quit()
it runs but, throws an error in Excel that says: "Document not saved. The document may be open, or an error may have been encountered when saving."
$path only points to a directory - this needs to be a full save path (including file name and extension) if you're going to pass it as an argument (see MSDN article on this method)
You can try and amend the existing filename and use that:
$saveFileName = $ExcelFile.Substring(0, $ExcelFile.LastIndexOf('.')) + .pdf"
Tried putting in the suggestion but, still says document not saved. Any idea what I'm doing wrong?
$path = "D:\Geoff's Files"
$dir = "D:\Geoff's Files\*.xls
$xlFixedFormat = Microsft.Office.Interop.Excel.xlFixedFormatType" -as [type]
$latest = Get-ChildItem -Path $dir | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$latest.name
$ExcelFile = "$latest"
$excel = New-Object -ComObject Excel.Application
$excel.visible = $true
$Workbook = $excel.workbooks.open($ExcelFile,2,$True)
$Worksheets = $Workbook.worksheets
$Worksheet = $Workbook.worksheets.Item(1)
$saveFileName = "ExcelFile.Substring(0, $ExcelFile.LastIndexOf('.')) + .pdf"
$Worksheet.ExportAsFixedFormat($xlFixedFormat::XlTypePDF, $saveFileName)
$workbook.Close()
$excel.Quit()
You need to use full path when referencing file and the use of double quotes in the saveFileName needs correcting. See the working code below:
$path = "D:\Geoff's Files"
$dir = "D:\Geoff's Files\*.xls
$xlFixedFormat = Microsft.Office.Interop.Excel.xlFixedFormatType" -as [type]
$latest = Get-ChildItem -Path $dir | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$latest.name
$ExcelFile = "$latest"
$excel = New-Object -ComObject Excel.Application
$excel.visible = $true
### needed to add path ### $Workbook = $excel.workbooks.open($ExcelFile,2,$True)
$Workbook = $excel.workbooks.open($path + "\" + $ExcelFile,2,$True)
$Worksheets = $Workbook.worksheets
$Worksheet = $Workbook.worksheets.Item(1)
### wrong use of quotes & needed to add path ### $saveFileName = "ExcelFile.Substring(0, $ExcelFile.LastIndexOf('.')) + .pdf"
$saveFileName = $ExcelFile.Substring(0, $ExcelFile.LastIndexOf('.')) + ".pdf"
$Worksheet.ExportAsFixedFormat($xlFixedFormat::XlTypePDF, $path + "\" + $saveFileName)
$workbook.Close()
$excel.Quit()

Resources