Automatically convert xls files to xlsx - excel

I have a software that just allows me to download my data in xls files but I want to use it as an xlsx file.
Currently I have an excel macro when I click on a button it converts all my xls files in xlsx but I want to automate this task so I don't have to open the excel file and click on the button.
I was thinking of a script that start when I log in windows or something like that, and it converts automatically my xls file when I download it. But I'm not very good with scripts so anyone can help me with that ? It's on windows 7 and 10.
Thank you for your help.
Edit:
Here is my Powershell script, now I have to automate it so that it runs automatically when I download a new .xls file, I know I can use the task scheduler but how can I do that automation on en event like adding a new xls file to a folder ? Or maybe we can do it in powershell ?
My script:
$xlFixedFormat = [Microsoft.Office.Interop.Excel.XlFileFormat]::xlOpenXMLWorkbook
write-host $xlFixedFormat
$excel = New-Object -ComObject excel.application
$excel.visible = $false
$folderpath = "C:\Users\Mgtspare\Downloads\"
$filetype ="*xls"
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()
remove-item $_.fullname
}
$excel.Quit()
$excel = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()
UPDATE:
I changed my script to have a new script faster and I put a watcher so I run the script when a new xls file is downloaded, I will use task manager to run this script when I log in windows so it can watch without doing anything.
Here is my new script:
### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "C:\Users\Mgtspare\Downloads"
$watcher.Filter = "*.xls"
$watcher.IncludeSubdirectories = $false
$watcher.EnableRaisingEvents = $true
### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
$action = {
$watcher.Path *.xls | rename-item -newname { [io.path]::ChangeExtension($_.name, "xlsx") }
}
### DECIDE WHICH EVENTS SHOULD BE WATCHED
Register-ObjectEvent $watcher "Created" -Action $action
while ($true) {sleep 5}
Issue:
My script run in the ISE but when i want to run it in cmd or with the right click on my script file and run with with powershell I have this issue
You must provide a value expression on the right-hand side of the '*' operator.

The below script automatically runs each morning with Task Manager
### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "C:\Users\Mgtspare\Downloads"
#$watcher.Filter = "*.*"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
$action = {
Get-ChildItem -Path C:\Users\Mgtspare\Downloads *.xls | rename-item -newname { [io.path]::ChangeExtension($_.name, "xlsx") }
}
### DECIDE WHICH EVENTS SHOULD BE WATCHED
Register-ObjectEvent $watcher 'Created' -SourceIdentifier 'FileCreated' -Action $action
while ($true) {sleep 1000}

Related

Powershell - Convert .XLS file to .XLSX

I am currently working on a powershell script that converts excel files from .xls to .xlsx
To be precise, I need this to work in some ways:
I need to catch the .xls files FROM a folder and make's a copy to a backup folder
converts them to .xlsx and uploads them to upload folder
Converting them from a folder and uploading them to another folder work's fine, but I tried to add some features and now I'm stuck.
This is the error when I try to run:
At C:\Users\Test\Conv_XLS_V2.ps1:40 char:2
+ }
+ ~ The Try statement is missing its Catch or Finally block. At C:\Users\Test\Conv_XLS_V2.ps1:20 char:16
+ ForEach-Object { ~
Missing closing '}' in statement block or type definition.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingCatchOrFinally
My code:
# set folders
$downloadfolder = "C:\Users\Test"
#$downloadfolder = "folder that gets the .xls files"
$uploadfolder = "C:\Users\Test\Upload"
#$uploadfolder = "folder that uploads the .xlsx files"
$backupfolder = "C:\Users\Test\Backup"
#$backupfolder = "folder that has .xls files as backup"
#open and convert xls to xlsx
Add-Type -AssemblyName Microsoft.Office.Interop.Excel
$xlFixedFormat = [Microsoft.Office.Interop.Excel.XlFileFormat]::xlOpenXMLWorkbook
write-host $xlFixedFormat
$excel = New-Object -ComObject excel.application
$excel.visible = $true
$filetype ="*xls"
Get-ChildItem -Path $folderpath -Include $filetype -recurse |
ForEach-Object {
try {
$xlsfilename = $_.fullname
#copy file to backup folder
Copy-Item -Path $xlsfilename -Destination $backupfolder
# open the xls
Write-Output "Converting $xlsfilename"
$workbook = $excel.workbooks.open($xlsfilename)
# save converted file (as xlsx)
$xlsxfilename = $xlsfilename + "x"
$workbook.saveas($xlsxfilename, $xlFixedFormat)
$workbook.close()
#remove old file
Write-Output "delete & move file(s)"
Remove-Item -Path $xlsfilename -Force
Move-Item -Path $xlsxfilename -Destination $uploadfolder -Force
# garbage collection
[gc]::collect()
[gc]::WaitForPendingFinalizers()
}
# close excel
$excel.Quit()
$excel = $null
Can someone have a look please?
The error message is clear. You forgot to close the try{..} block with an ending bracket } and a try{..} should be followed up by either one or more catch{..} blocks and optionally a finally{..} block.
You can read about that on about Try Catch Finally.
Then, there are some other things wrong and/or can be improved upon in your code as well.
$folderpath is not defined and should be the source folder $downloadfolder
use -Filter instead of -Include as it is much faster. Also you have left out the dot in '*.xls'
append switch -File to the Get-ChildItem cmdlet to make sure you will not receive and try to process directories as well
you can save the converted .xlsx files directly to the uploadfolder, no need to create first and then move
to remove the used COM objects, release them from memory first and then initiate the Garbage Collect.
Do this after you have quit Excel.
# set folders
$downloadfolder = "C:\Users\Test" # folder where the .xls files are
$uploadfolder = "C:\Users\Test\Upload" # folder that uploads the .xlsx files
$backupfolder = "C:\Users\Test\Backup" # folder that has .xls files as backup
# open and convert xls to xlsx
Add-Type -AssemblyName Microsoft.Office.Interop.Excel
$xlFixedFormat = [Microsoft.Office.Interop.Excel.XlFileFormat]::xlOpenXMLWorkbook
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false # it is much faster if Excel is not visible
# loop through the .xls files and process them
Get-ChildItem -Path $downloadfolder -Filter '*.xls' -Recurse -File |
ForEach-Object {
try {
$xlsfilename = $_.FullName
#copy file to backup folder
Copy-Item -Path $xlsfilename -Destination $backupfolder -Force
# open the xls
Write-Host "Converting $xlsfilename"
$workbook = $excel.Workbooks.Open($xlsfilename)
# save converted file (as xlsx) directly to the upload folder
$newfilename = Join-Path -Path $uploadfolder -ChildPath ('{0}.xlsx' -f $_.BaseName)
$workbook.SaveAs($newfilename, $xlFixedFormat)
$workbook.Close()
#remove old file
Write-Host "Delete old file '$xlsfilename'"
Remove-Item -Path $xlsfilename -Force
}
catch {
# write out a warning as to why something went wrong
Write-Warning "Could not convert '$xlsfilename':`r`n$($_.Exception.Message)"
}
}
# close excel
$excel.Quit()
# garbage collection
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
The error describe a syntax issue. You have included a try { statement without closing it with a } catch {} block. That's all.

I cant get powershell to run in task scheduler

I am trying to run the following Powershell script via the Task Scheduler but although it gives me a successful run (0x0), nothing happens. If I run the script manually from the same machine as a standard user the script executes without any issue.
$folderpath = "\\shared_path\excel.xls"
Add-Type -AssemblyName Microsoft.Office.Interop.Excel
$xlFixedFormat = [Microsoft.Office.Interop.Excel.XlFileFormat]::xlOpenXMLWorkbook
write-host $xlFixedFormat
$excel = New-Object -ComObject excel.application
$excel.visible = $false
$filetype ="*xls"
Get-ChildItem -Path $folderpath -Include $filetype -recurse |
ForEach-Object `
{
$path = ($_.fullname).substring(0, ($_.FullName).lastindexOf("."))
$workbook = $excel.workbooks.open($_.fullname)
$path += ".xlsx"
$excel.DisplayAlerts = $false;
$workbook.saveas($path, $xlFixedFormat)
$workbook.close()
}
$excel.Quit()
$excel = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()
Just to clarify, I have tried different options in task scheduler such as running it under arguments like "-WindowStyle Hidden -NonInteractive -ExecutionPolicy Bypass -File "C:\Temp\powershell.ps1" or SYSTEM and currently logged on user but nothing made a difference.
My system is a domain joined Windows 10 with unrestricted access to the shared location (Everyone access)
Any ideas?
I usually configure Task Scheduler to run a PowerShell script with the following settings:
New Action
Action: "Start a program".
Program/script: "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
Add arguments (optional): .\ScriptName.ps1 -Arg1 Value1
Start in (optional): Path\to\Script\Directory

How do I stop powershell from watching a file for a few seconds?

I'm writing something in powershell to watch file 'A' for changes and open file 'B' when it does. The only problem is the excel file (B) has vba code to run on open that has to copy over the data on file 'A.' From my research it seems like I have to open file 'A' to do this since it has to be a .xlsx and opening it starts a continuous loop.
I've tried the sleep command, but it seems like it's still watching the file during or before the sleep, and then just opens the file again once it has waited the amount of time I tell it.
How do I make the watcher stop watching for just a minute or so?
Here is the code I'm working with currently:
Function Register-Watcher {
param ($folder)
$filter = "*.xlsx"
$folder = "\\powershell\watcher\test\folder"
$watcher = New-Object IO.FileSystemWatcher $folder, $filter -Property #{
IncludeSubdirectories = $false
EnableRaisingEvents = $true
}
$changeAction = [scriptblock]::Create('
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Write-Host "The file $name was $changeType at $timeStamp"
$Excel = New-Object -ComObject Excel.Application
$Excel.Workbooks.Open("\\powershell\watcher\test\folder\fileB.xlsm")
sleep 60
')
Register-ObjectEvent $Watcher "Changed" -Action $changeAction
}
Register-Watcher "\\powershell\watcher\test\folder\fileA.xlsx"
$Change
You can stop it from raiseing events using
$watcher.EnableRaisingEvents = $false
You can read more about it here from Microsoft
https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher.enableraisingevents?view=netframework-4.7.2#System_IO_FileSystemWatcher_EnableRaisingEvents

Rename XSL without save prompt using PowerShell

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)

Using Parameters in Powershell to assign folder locations

I'm new to powershell and I need some help here. Below is a script I wrote to locate an excel file in folder. The files in the excel sheet would be compared to the contents of another folder on the same machine. Locations are : "C:\MKS_DEV\" and The resultant matched files would be zipped and put in another location as shown in the scripts. These scripts would be used by other people on different machines so the locations of both folders could differ on different machines.
I want to write an argument or using parameters for the location of both folders so that I wouldn't have to specify the location all the time I have to run the scripts and cant figure out how to implement this.
The scripts works perfectly but I just need to incorporate arguments/parameters into it. Any help would be very much appreciated.
Thanks.
Here is the code:
# Creating an object for the Excel COM addin
$ExcelObject = New-Object -ComObject Excel.Application
# Opening the Workbook
$ExcelWorkbook = $ExcelObject.Workbooks.Open("C:\Eric_Source\Test.xls")
# Opening the Worksheet by using the index (1 for the first worksheet)
$ExcelWorksheet = $ExcelWorkbook.Worksheets.Item(1)
# The folder where the files will be copied/The folder which will be zipped
# later
$a = Get-Date
$targetfolder = "C:\"+$a.Day+$a.Month+$a.Year+$a.Hour+$a.Minute+$a.Second
# Check if the folder already exists. Command Test-Path $targetfolder returns
# true or false.
if(Test-Path $targetfolder)
{
# delete the folder if it already exists. The following command deletes a
# particular directory
Remove-Item $targetfolder -Force -Recurse -ErrorAction SilentlyContinue
}
# The following command is used to create a particular directory
New-Item -ItemType directory -Path $targetfolder
# Declaration of variables, COlumn value = 6 for Column F
$row = 1
$col = 6
# Read a value from the worksheet with the following command
$filename = $ExcelWorksheet.Cells.Item($row,$col).Value2
$filename
# change the folder value below to specify the folder where the powershell
# needs to search for the filename that it reads from excel file.
$folder = "C:\MKS_DEV\"
$null = ""
You have multiple ways to parameter your script.
The first one is to us $args[n] [automatic variable]1.
If your script is called MyScript.PS1 you can call it with :
MyScript.PS1 "C:\Eric_Source\Test.xls"
Then inside your script use $args[0] for the first argument.
Another way is to use the reserved word Param at the begining of your script:
Param ($MyParam1, $MyParam2)
When you call your script $MyParam1 will contain the first param and so on.
You could create it as a function and load it.
Function Folder-Deletion ($ExcelWorkbook,$targetfolder) {
$ExcelObject = New-Object -ComObject Excel.Application
$ExcelOpen = $ExcelObject.Workbooks.Open($ExcelWorkbook)
$ExcelWorksheet = $ExcelOpen.Worksheets.Item(1)
$a = Get-Date
if(Test-Path $targetfolder)
{
Remove-Item $targetfolder -Force -Recurse -ErrorAction SilentlyContinue
}
New-Item -ItemType directory -Path $targetfolder
$row = 1
$col = 6
$filename = $ExcelWorksheet.Cells.Item($row,$col).Value2
$filename
}
Then run the function against the spreadsheet and folder like so:
Folder-Deletion C:\Eric_Source\Test.xls C:\MKS_DEV
Or you could create a PowerShell script file (e.g. FolderDeletion.ps1) with the following contents:
param($ExcelWorkbook,$targetfolder)
$ExcelObject = New-Object -ComObject Excel.Application
$ExcelOpen = $ExcelObject.Workbooks.Open($ExcelWorkbook)
$ExcelWorksheet = $ExcelOpen.Worksheets.Item(1)
$a = Get-Date
if(Test-Path $targetfolder)
{
Remove-Item $targetfolder -Force -Recurse -ErrorAction SilentlyContinue
}
New-Item -ItemType directory -Path $targetfolder
$row = 1
$col = 6
$filename = $ExcelWorksheet.Cells.Item($row,$col).Value2
$filename
Then run the script against the spreadsheet and folder like so:
FolderDeletion.ps1 C:\Eric_Source\Test.xls C:\MKS_DEV

Resources