I have a file which file extension is .xls, but the weird thing is when save as, the default is go to html file.
This caused a problem when I'm using my powershell script to perform some action on the file and saved it as xls file. The result is the script generate one xls file and one folder which contain html file. Is there a way to fix this issues ? I just want to save the file as xls file wihout any html files. Any helps are much appreciated.
Here the code that I use to save file as .xls
Function RenameTab ($ExcelFileName, $OutLoc)
{
$excelFile = "D:\Projects\" + $excelFileName + ".xls"
$xldoc = New-Object -ComObject "Excel.Application"
$xldoc.Visible = $false
$xldoc.DisplayAlerts = $false
$workbook = $xldoc.Workbooks.Open($excelFile)
foreach ($worksheet in $workbook.Worksheets)
{
$n = $ExcelFileName ##+ "_" + $worksheet.Name
$worksheet = $workbook.worksheets.item(1)
$worksheet.name = "Sheet1"
$workbook.SaveAs($OutLoc + $n + ".xlsx")
$workbook.Close()
}
$xldoc.Quit()
}
When you call SaveAs, you should really specify the file format.
According to the documentation for SaveAs, it would be the second argument, so the corrected line would be:
$workbook.SaveAs($OutLoc + $n + ".xlsx", 51) # 51 = xlOpenXMLWorkbook
I don't have all that much experience with COM interaction, but you might be able to use the enumeration directly somehow and make the line similar to:
$workbook.SaveAs($OutLoc + $n + ".xlsx", $xldoc.XlFileFormat.xlOpenXMLWorkbook)
Related
I am trying to save a specific Excel Sheet from a Macro Enabled Excel Workbook (xlsm) via Powershell to csv to upload it into a database. This is done via Powershell since it needs to be automated along with some more data processing etc.
The Situation:
I have a list of excel files in a directory, each having the same structure/sheets.
I parse each file to a Powershell function which creates a new Excel Object and opens the workbook.
In the next step I am trying to save a specific sheet (here Sheet with Index 2)
The Problem:
Iterating through each Worksheet and saving them gives me all Sheets including the one I am looking for (Sheet 2)
Accessing Sheets 2 via $ws = $wb.Worksheets(2) also gives me the right Sheet (according $ws.name) but saving $ws via $ws.SaveAs("$destinationDirectory" + $File.BaseName + ".csv", 6) results in a csv file containing Sheet 1.
I have saved Worksheets with basically exactly the same code successfully before (except instead of xlsm I was dealing with xlsx).
Code
Function ExcelToCsv ($File) {
echo "Converting $($File.Name) to csv..."
$Excel = New-Object -ComObject Excel.Application;
$Excel.DisplayAlerts = $False;
$wb = $Excel.Workbooks.Open($File)
$ws = $wb.Worksheets(2)
echo "ws is:" + $ws.name # Correctly printing Worksheet name of Sheet 1
$ws.SaveAs("$destinationDirectory" + $File.BaseName + ".csv", 6) # Saving Sheet 1 instead of Sheet 2
$wb.Close($True);
}
}
foreach ($file in $files){
ExcelToCsv -File $file;
}
Workaround
My current workaround is to iterate through the sheets via foreach
$n = 1
foreach($ws in $wb.Worksheets){
$ws.SaveAs("$destinationDirectory" + $File.BaseName + "-$($n).csv", 6)
$n = $n+1
}
And deleting any unwanted sheets (recognized by $n != 2) via
Remove-Item "$($destinationDirectory)*-[13456789].csv";
Which works but is not really optimal.
Also: checking for $n -eq 2 in the foreach and only saving that sheet also does not work since it will simply save sheet 1 again.
And ideas are greatly appreciated!
After a frustrating afternoon I eventually worked this one out - you need to call Activate against the sheet you want to save e.g.
$sheet = $book.sheets.item('User_Specified_Report')
$Sheet.Activate()
$sheet.SaveAs($newName,6)
I have this excel sheets and I want to have the same format for csv files. Could some one help me with a automation script please (to convert multiple excel sheets to csv files)??
I tried this script, but the 16th digit of the card number is turning to be zero as excel can read only 15 digits right. Can we modify this code to convert multiple excel sheets to csv files?
Could someone help me with this.
Convert Excel file to CSV
$xlCSV=6
$Excelfilename = “C:\Temp\file.xlsx”
$CSVfilename = “C:\Temp\file.csv”
$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}
Excel is really particular in its handling of CSV files..
Although the 16 digit numbers are written out in full when using the SaveAs method, if you re-open it by double-clicking the csv file, Excel screws up these numbers by converting them to numeric values instead of strings.
In order to force Excel to NOT interpret these values and simply regard them as strings, you need to adjust the values in the csv file afterwards, by prefixing them with a TAB character.
(this will make the file useless for other applications..)
Of course, you need to know the correct column header to do this.
Let's assume your Excel file looks like this:
As you can see, the value we need to adjust is stored in column Number
To output csv files on which you can double-click so they are opened in Excel, the code below would do that for you:
$xlCSV = 6
$Excelfiles = 'D:\test.xlsx', 'D:\test2.xlsx' # an array of files to convert
$ColumnName = 'Number' # example, you need to know the column name
# create an Excel COM object
$Excel = New-Object -comobject Excel.Application
$Excel.Visible = $False
$Excel.DisplayAlerts = $False
foreach ($fileName in $Excelfiles) {
$Workbook = $Excel.Workbooks.Open($fileName)
# use the same file name, but change the extension to .csv for output
$CSVfile = [System.IO.Path]::ChangeExtension($fileName, 'csv')
# have Excel save the csv file
$Workbook.SaveAs($CSVfile, $xlCSV)
$Workbook.Close($false)
}
# close excel and clean up the used COM objects
$Excel.Quit()
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
# now import the csv files just created and update the card number
# column by prefixing the value with a TAB character ("`t").
# this will effectively force Excel NOT to interpret the value as numeric.
# you better not do this inside the same loop, because Excel keeps a lock
# on outputted csv files there.
foreach ($fileName in $Excelfiles) {
# use the same file name, but change the extension to .csv for output
$CSVfile = [System.IO.Path]::ChangeExtension($fileName, 'csv')
# the '-UseCulture' switch makes sure the same delimiter character is used
$csv = Import-Csv -Path $CSVfile -UseCulture
foreach ($item in $csv) { $item.$ColumnName = "`t" + $item.$ColumnName }
# re-save the csv file with updates values
$csv | Export-Csv -Path $CSVfile -UseCulture -NoTypeInformation
}
I have the following code but I am trying to get it to prompt user with a dynamic dialog box to get the output file to "save as".
$pathtsv = "c:\test.txt"
$pathxlsx = "c:\NBP ESP-152 REV F TEMPLATE.xlsx"
$Excel = New-Object -ComObject "Excel.Application"
$Excel.Visible=$true
$Workbook = $Excel.Workbooks.Open($pathxlsx) # Open Template
$TempWorkbook = $Excel.Workbooks.Opentext($pathtsv) # Open text file in excel
$temp = $excel.Workbooks.Item(2) #select workbook with text
$temp = $temp.Worksheets.Item(1) #select text worksheet
$CopyRange = $temp.Range("A1:G8") #set range
$CopyRange.Copy() #copy data
$workbooksheet = $Workbook.Worksheets.Item(1)#sets doc to copy to
$Workbooksheet.activate()
$PasteRange = $workbooksheet.Range("A3:J10") #sets range
$workbooksheet.Paste($PasteRange)#paste data
#save and close the workbook
$Workbook.Close($true)
$Excel.Quit()
while( [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)){}
[gc]::collect()
[gc]::WaitForPendingFinalizers()
So I tried adding in:
$SaveFileDialog = New-Object windows.forms.savefiledialog
$SaveFileDialog.initialDirectory = [System.IO.Directory]::GetCurrentDirectory()
but it does not seem to be working. Not sure why, maybe because I am still newbish to powershell scripting. I just want to get it to end on the save as dialog box, would be fine. Do I have to cut the end of the original out? (IE. does this get cut out?
$Workbook.Close($true)
$Excel.Quit()
while( [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)){}
[gc]::collect()
[gc]::WaitForPendingFinalizers()
)
You need to do a few things to make this work. First, add a reference to System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms
After your copy/paste operations create the dialog and set a couple properties
$SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
$SaveFileDialog.initialDirectory = [System.IO.Directory]::GetCurrentDirectory()
$SaveFileDialog.Filter = 'All files (*.*)|*.*'
This is where you will need to decide what you want to do when showing the dialog. Do you allow the user to cancel, how do you handle empty paths, incorrect extensions, etc... For the sake of example, I am going to simply continue to show it until a path is chosen. I am not endorsing this as a good choice.
$dialogResult = $null
while($dialogResult -ne "OK"){
$dialogResult = $SaveFileDialog.ShowDialog()
}
You can then access the selected path with the FileName property and perform and checks needed on it
$SaveFileDialog.FileName
Pass this to the excel workbook's SaveAs method. See this answer for details on this method https://stackoverflow.com/a/25289878/3594883
$WorkBook.SaveAs($SaveFileDialog.FileName, 51, [Type]::Missing, [Type]::Missing, $false, $false, 1, 2)
Finally close your workbook and excel. Perform cleanup etc.
I am using EPPlus.dll in Powershell script to convert csv file to xlsx file, but I no have idea how to save file without overwrite existing data.
I have to generate new information from csv file to excel file,
but I have others, manualy created tab which my script always overwrite.
Script looks like this:
$FileNameCSV = "$env:WORKSPACE/file.csv"
$FileNameExcel = "\\folder\file.xlsx "
$DLLPath = "D:\EPPlus.dll"
[Reflection.Assembly]::LoadFile($DLLPath) | Out-Null
$Format = New-object -TypeName OfficeOpenXml.ExcelTextFormat
$Format.Delimiter = ","
$Format.TextQualifier = '"'
$Format.Encoding = [System.Text.Encoding]::UTF8
$Format.SkipLinesBeginning = '0'
$Format.SkipLinesEnd = '1'
$TableStyle = [OfficeOpenXml.Table.TableStyles]::Medium1
$ExcelPackage = New-Object OfficeOpenXml.ExcelPackage
$Worksheet = $ExcelPackage.Workbook.Worksheets.Add("CM")
$null=$Worksheet.Cells.LoadFromText((Get-Item $FileNameCSV),$Format,$TableStyle,$true)
$Worksheet.Cells[$Worksheet.Dimension.Address+1].AutoFitColumns()
$Worksheet.Column(3).Style.Numberformat.Format = "dd/mm/yyyy";
$ExcelPackage.SaveAs($FileNameExcel)
Write-Host "CSV File $FileNameCSV converted to Excel file $FileNameExcel"
You need to open the existing file named $FileNameExcel by passing the name to the constructor, probably like this:
$ExcelPackage = New-Object OfficeOpenXml.ExcelPackage($FileNameExcel)
Then, add a new worksheet to this workbook, load the csv into this new worksheet the same way you do it currently, and save the workbook again.
I'm having trouble saving one worksheet from Excel workbook as a single .htm file.
I know if I open up Excel, open my workbook, select the worksheet I want, and do a "Save As" file type .htm it will work. Every time I code it I get "Hitlist.htm" plus a folder named "Hitlist_files" with all the style sheets, etc. Unfortunately, in powershell it does the "save as" with all the other data and supplemental files (extra folders, styling specifications, etc.)
Help. Code below.
#Create and get my Excel Obj
$excel = New-Object -comobject Excel.Application
$excel.visible=$false
$excel.DisplayAlerts=$false
$UserWorkBook = $excel.Workbooks.Open("e:\hitlist\hitlist.xlsx")
#Select first Sheet
$UserWorksheet = $UserWorkBook.Worksheets.Item(1)
#HitList File name and type
$hitlist = "E:\HitList\Hitlist.htm"
$xlHtml = 44
#Save, close, and clean up
#I tried this too...no go - $UserWorkBook.SaveAs($hitlist,$xlHtml)
$UserWorksheet.SaveAs($hitlist,$xlHtml)
$UserWorkBook.close()
$excel.quit()
$excel = $null
I've adjusted your code to your expectations again, after we clarified some more. In particular, look at any line denoted with #changed-grav for my modifications to your existing code, or the very end (#added-grav) for some additional steps I added to fit the exact specs:
(This was tested fully, and appears to be working exactly as you requested - but I did some modifications for my testing, so let me know if I didn't change a value back that you needed)
#Create and get my Excel Obj
$excel = New-Object -comobject Excel.Application
$excel.visible=$false
$excel.DisplayAlerts=$false
$UserWorkBook = $excel.Workbooks.Open("e:\hitlist\hitlist.xlsx")
#Select first Sheet
$UserWorksheet = $UserWorkBook.Worksheets.Item(1)
#HitList File name and type
$hitlistCSV = "e:\hitlist\hitlist.csv" #changed-grav
$hitlistHTML = "e:\hitlist\hitlist.htm" #changed-grav
$xlCSV = 6 #changed-grav
#Save, close, and clean up
$UserWorksheet.SaveAs($hitlistCSV,$xlCSV) #changed-grav
$UserWorkBook.close()
$excel.quit()
$excel = $null
#new functionality, to import the CSV and then export as HTM
#added-grav START
$htmlData = Get-Content $hitlistCSV | ConvertFrom-CSV | ConvertTo-HTML
Set-Content $hitlistHTML $htmlData
Remove-Item $hitlistCSV
#added-grav END