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)
Related
I am having multiple excel files having same no of sheets with same sheet names. The same sheets in all excel files having the same headers. So I want an idea how to do merging for all the matched sheets in multiple excel files and create a new excel file via scripting using Powershell.
Any sugguestion Helps.
Thanks.
If the number of columns is the same across the different Excel worksheets then you should be able to use the below code to merge the files together.
The code uses methods from the NamedRange interface from the Excel API.
Example Code: (Just remember to change the paths and file names to your environment)
# Create an instance of Excel
$Excel = New-Object -ComObject Excel.Application
# Find the files you want to process
$Files = Get-ChildItem -Path C:\Temp -Filter *.xlsx
# Create a target workbook and worksheet called 'Sheet1'
$TargetWorkbook = $Excel.Workbooks.add()
$TargetWorksheet = $TargetWorkbook.Sheets.Item("Sheet1")
# Loop through our Excel files
foreach($File in $Files) {
# Open the workbook and get the first sheet
$SourceWorkbook=$Excel.Workbooks.Open($File.FullName)
$SourceWorksheet=$SourceWorkbook.Sheets.Item(1)
# Calculate the end column letter
$EndColumn = [char]([int][char]'A' + $SourceWorksheet.UsedRange.Columns.Count - 1)
# Activate the source worksheet
$SourceWorksheet.activate()
# Get the total number of rows in the sheet
$SourceLastRow = $SourceWorksheet.UsedRange.Rows.Count + 1
# Calculate what our start row should be
# A1 for the first worksheet only to include the headers
$StartRow = (& { If ($TargetWorkSheet.UsedRange.rows.count -eq 1) { "A1" } Else { "A2" } } )
# Get the range of data and copy it to the clipboard
$SourceRange = $SourceWorksheet.Range("$($StartRow):$EndColumn$SourceLastRow")
$SourceRange.copy()
# Activate the target worksheet
$TargetWorksheet.activate()
# Get the total number of rows in the sheet
$TargetLastRow = $TargetWorkSheet.UsedRange.Rows.Count
if ($TargetWorkSheet.UsedRange.Rows.Count -ne 1) {
# If this isn't the first sheet we've processed, add one additional row
$TargetLastRow++
}
# Get the target range and paste the data
$TargetRange = $TargetWorksheet.Range("A$($TargetLastRow):$EndColumn$($SourceRange.Rows.Count)")
$TargetWorksheet.Paste($TargetRange)
# Disable showing alerts, otherwise a notification about
# large amounts of data on the clipboard will be shown
$Excel.DisplayAlerts = $false
# Close the source workbook
$SourceWorkbook.Close()
}
# Re-enable showing alerts
$Excel.DisplayAlerts = $true
# Save the workbook to the desired path
$TargetWorkbook.SaveAs("C:\Temp\Merged.xlsx")
# Quit Excel
$Excel.Quit()
If your sheets have different numbers of columns, you could still use the above code, however you'll need to make some changes to the $SourceRange, $TargetRange and $EndColumn variables to account for this.
I'm trying to find out if an excel sheet contains an array (in any cell in the fourth sheet). The variable is a user input as shown:
$j = Read-Host "Enter sensor serial number"
$Sens_name = #("$j")
And the act of it trying to find the input looks like this, where $EPRB2_loca is the location of the excel file including file extension:
#Checking in EPRB 2 file
$Excel = New-Object -ComObject Excel.Application
$Workbook = $Excel.Workbooks.Open("$EPRB2_loca")
$WorkSheet = $Workbook.Sheets.Item(4)
$WorkSheet.Name
$EPRB2_file = $WorkSheet.Cells.Find("$Sens_name")
if ($EPRB2_file.HasArray -eq $false)
{
$EPRB2_file = $null
}
Can someone please help me figure out why it won't show as it containing the array when I know it does?
I was using the wrong command.
If anyone comes across this, use .Count instead of .HasArray
I have this Powershell script i'm trying to combine multiple workbooks with single sheets onto one workbook with a single sheet and combine them all on the one sheet. I can't get past the fact it keeps telling me there is no file named $destfile and can't be opened. What is the correct syntax for that?
Thanks
$ExcelObject = New-Object -ComObject excel.application
$ExcelObject.visible=$true
$file1 = 'file1location'
$file2 = 'file2location'
$destfile = 'fileI want to saveas afterits compiled'
$xl = new-object -c excel.application
$xl.displayAlerts = $false # don't prompt the user
$wb1 = $xl.workbooks.open($file1, $null, $true) # open source, readonly
$wb2 = $xl.workbooks.open($file2, $null, $true)
$wb3 = $xl.workbooks.open($destfile) # open target
$sh1_wb2 = $wb2.sheets.item(1) # first sheet in destination workbook
$sheetToCopy = $wb1.sheets.item('Sheet1') # source sheet to copy
$sheetToCopy.copy($sh1_wb2) # copy source sheet to destination workbook
$wb1.close($false) # close source workbook w/o saving
$wb2.close($true) # close and save destination workbook
$xl.quit()
spps -n excel
You can try to use https://github.com/dfinke/ImportExcel
Export data to csv from multiple worksheets with Import-CSV, then combine those CSV files (i suppose that they have identical rows) and import combined CVS back to excel using Export-CSV... quite simple. and does not require any COM manipulations.
I am trying to automate the following manual task, and am struggling with part of it:
1) Open a text file that contains multiple lines containing data.
2) Copy the contents of this file to the clipboard.
3) Open and Excel spreadsheet.
4) Rename the spreadsheet to Test.
5) Paste the contents of the clipboard.
When this is done manually the content is pasted and each line in the text file is inserted as a new row in column A.
Originally the customer wanted all of the file content to be injected into cell A1. I was able to achieve this with the below PowerShell code.
However they have since changed this back to wanting each line of text to go into a separate row in column A.
I cannot figure out how to do this gracefully via the Get-Content method of copying out the text data. I have seen workarounds to this issue whereby Excel opens the text file and copies the text into an intermediate workbook and then into the final workbook.
Could someone please let me know if it's possible to amend my already working code below so that it adds the text to rows in column A rather than to cell A1?
# Clear the screen of any previous text.
cls
$ExcelFile="C:\Users\User\Desktop\Test\Test.xlsx"
$TextFile="C:\Users\User\Desktop\Test\TestText.txt"
$Content = Get-Content $TextFile -Raw
# Perform operations in Excel based on content of the downloaded file.
$Excel = New-Object -ComObject Excel.Application
# For troubleshooting enable the below to view Excel as file is manipulated:
#$Excel.Visible=$true
# Disable Excel alerts. Hash this line out for troubleshooting.
$Excel.DisplayAlerts = $false
# Set up workbook...
$Workbook = $Excel.Workbooks.Add()
$Data = $Workbook.Worksheets.Item(1)
$Data.Name = 'Test'
# Insert Data
$Data.Cells.Item(1,1) = "$Content"
# Format, save and quit excel
$UsedRange = $Data.UsedRange
$UsedRange.EntireColumn.AutoFit() | Out-Null
$Workbook.SaveAs("$ExcelFile")
$Excel.Quit()
I know that the part I would need to change is as follows, but I'm not sure what to change it to:
# Insert Data
$Data.Cells.Item(1,1) = "$Content"
Many thanks in advance.
To do this, you need to find the last used row in the sheet and write each line from there:
$ExcelFile = "C:\Users\User\Desktop\Test\Test.xlsx"
$TextFile = "C:\Users\User\Desktop\Test\TestText.txt"
# Perform operations in Excel based on content of the downloaded file.
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $false
$Excel.DisplayAlerts = $false
# open the file and select the first worksheet
$WorkBook = $Excel.Workbooks.Open($ExcelFile)
$WorkSheet = $Workbook.Worksheets.Item(1)
# get the first unused row
$row = ($WorkSheet.UsedRange.Rows).Count + 1
# fill in the data
Get-Content -Path $TextFile | ForEach-Object {
$WorkSheet.Cells.Item($row++, 1) = $_
}
# format column A and save the file
$UsedRange = $WorkSheet.UsedRange
$UsedRange.EntireColumn.AutoFit() | Out-Null
$WorkBook.Save()
# quit excel and clean up the used COM objects
$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()
I think the solution would be to read each line in content by for or foreach loop
in loop, write the line's content into the last row of column A in the excel file.
It's will be something like this
foreach($line in $Content){
$Data.Cells.Item($LastRow,1) = $line
}
I'm an end user who hosts a public Excel 2010 Workbook (without macros, so other users can feel safe) which contains all of the individual player stats for the Madden NFL Mobile game by EA Sports.
I've also created a non-public Macro-Enabled 2010 Workbook which I use to automate the extraction all of the relevant data from a 3rd party website and reparse all of that data into a spreadsheet layout I desire.
My first column of the Macro-Enabled Workbook contains the player's name with a hyperlink to that player's webpage on that 3rd party website, and the macro creates that hyperlink for me just fine.
When I use a Data Connection to automate the syncing of the data from the Macro Book to the Non-Macro Book, everything copies fine, except for the player's name which is only in plain text (no hyperlink).
I also tried to make that first column an excel hyperlink formula (instead of VBA's hyperlink function), but the formula won't transfer via the data connection either.
Is there something I can edit maybe inside of the Data Connection file (.odc) to accomplish my goal?
My only other workaround so far is to add 2 extra hidden columns containing the text link and player name, and then mucking around with the destination Table to make the first column a pre-defined excel HYPERLINK formula to convert the two columns back into a hyperlink.
Any ideas?
Nevermind, I decided to just use a powershell script and batch file to do all of my copy/pasting, and skipping the whole Data Connection thing all together.
My "TransferMaddenData.ps1" PowerShell Code:
$path = "C:\Users\Grego\Desktop\MaddenData.xlsm"
$Excel = New-Object -ComObject excel.application
$Excel.visible = $false
$Workbook = $Excel.Workbooks.open($path)
$Worksheet = $Workbook.WorkSheets.item("DataOutput")
$Rows = $Worksheet.UsedRange.Rows.Count
$Rows += 1
$Cells = "A3:BD" + $Rows
$Worksheet.activate()
$range = $WorkSheet.Range($Cells)
$range.Copy() | out-null
$path = "C:\Users\Grego\Desktop\Madden1.xlsm"
$Workbook2 = $Excel.Workbooks.open($path)
$Worksheet = $Workbook2.Worksheets.item("Raw Data")
$range = $Worksheet.Range("A3")
$Worksheet.Paste($range)
$Workbook2.Save()
$Workbook.Close()
$Excel.Quit()
Remove-Variable -Name excel
[gc]::collect()
[gc]::WaitForPendingFinalizers()
This is my "FixMadden.bat" batch file:
#echo off
color 1F
echo.
C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File "C:\Users\Grego\Desktop\TransferMaddenData.ps1"
:EOF
echo Waiting seconds
timeout /t 10 /nobreak > NUL
Hope this maybe helps someone else someday!