Convert code from Excel Macro to PowerShell - excel

I open a CSV file in Excel and run this macro to change the background color. I am trying to convert this part of code to PowerShell.
lrow = Range("G" & Rows.Count).End(xlUp).Row
Set MR = Range("G2:G" & lrow)
For Each cell In MR
If UCase(Trim(cell.Value)) = "FALSE" Then
cell.Interior.ColorIndex = 3
End If
Next
Any help converting this code to PowerShell.
Thanks
SR

You could write something like this:
$objExcel = New-Object -ComObject Excel.Application
$objExcel.Visible = $true
$objExcel.DisplayAlerts = $false
$filePath = "c:\logs\2015-04-23.csv"
$xlsFilePath = Get-Item -Path $filePath | % { Join-Path (Split-Path $_ -Parent) "$($_.BaseName).xls" }
$workBook = $objExcel.Workbooks.Open($filePath)
$workSheet = $WorkBook.sheets | select -First 1
$xlup = -4162
$lrow = $workSheet.cells.Range("G" + $workSheet.Rows.Count).End($xlup).Row
$workSheet.cells.Range("G2:G" + $lrow) | % {
$value = $_.Text
if($value.ToUpper() -eq "TRUE"){
$_.Interior.ColorIndex = 3
}
}
$WorkBook.SaveAs($xlsFilePath, 18)
$objExcel.Quit()
If you have a very large file, it is faster to search values using powershell then updating the Excel sheet. The following example looks a bit funny but executes much faster.
$filePath = "c:\logs\2015-04-23.csv"
$rowAliases = 97..122 | foreach { ([char]$_).ToString().ToUpper() }
$selectedRow = "G"
$selectedName = (Get-Content $filePath -ReadCount 1 -TotalCount 1).Split(",")[$rowAliases.IndexOf($selectedRow)]
$startRow = 2
$rowCount = 1;
$objExcel = New-Object -ComObject Excel.Application
$objExcel.Visible = $true
$objExcel.DisplayAlerts = $false
$xlsFilePath = Get-Item -Path $filePath | % { Join-Path (Split-Path $_ -Parent) "$($_.BaseName).xls" }
$workBook = $objExcel.Workbooks.Open($filePath)
$workSheet = $WorkBook.sheets | select -First 1
Import-Csv -Path $filePath | % {
if($rowCount -ge $startRow){
[string]$value = $_ | select -ExpandProperty $selectedName
if($value.ToUpper() -eq "TRUE"){
$workSheet.cells.Item($rowCount + 1, $selectedIndex + 1).Interior.ColorIndex = 3
}
}
$rowCount ++
}
$WorkBook.SaveAs($xlsFilePath, 18)
$objExcel.Quit()

Related

Having difficult creating excel sheet while looping thru names

I have bunch of server names. Then i am looping thru these names and creating sheet by each server name. But getting error. below is my code
create excel spreadsheet
$xlsx = Join-Path -Path (Get-Location).Path -ChildPath "ClusterUsageReport-$(get-date -UFormat '%Y-%m-%d-%H-%M-%S').xlsx"
$xl = new-object -ComObject Excel.Application
$workbook = $xl.Workbooks.Add()
$i = 1
$clusters = ('Hcohesity01','Hcohesity05')
foreach ($vip in $clusters){
### create excel woorksheet
$worksheet = $workbook.Worksheets.Item($i)
$worksheet.Name = "$vip-Storage Growth"
$worksheet.activate | Out-Null
$i ++
### there are more scripts below this line, which create charts in each sheet ####
}
$xl.visible = $true
$worksheet.columns.autofit() | Out-Null
$worksheet.usedRange.rows(1).Font.Bold = $True
$workbook.SaveAs($xlsx,51) | Out-Null
Message i am getting
Invalid index. (Exception from HRESULT: 0x8002000B (DISP_E_BADINDEX))
At C:\raju\scripts\cluster_usage_in_excel.ps1:25 char:1
$worksheet = $workbook.Worksheets.Item($i)
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
Message coming from line $worksheet = $workbook.Worksheets.Item($i) , any idea how do i achieve this ?
Below is entire script .. can you point where needs update ??
usage: ./graphStorageGrowth.ps1 -vip mycluster -username myuser [ -domain mydomain.net ] [ -days 60 ]
### process commandline arguments
[CmdletBinding()]
param (
[Parameter(Mandatory = $True)][string]$username,
[Parameter()][int32]$days = 60
)
### constants
$TB = (1024*1024*1024*1024)
$GB = (1024*1024*1024)
### source the cohesity-api helper code
. ./cohesity-api
### create excel spreadsheet
$xlsx = Join-Path -Path (Get-Location).Path -ChildPath "ClusterUsageReport-$(get-date -UFormat '%Y-%m-%d-%H-%M-%S').xlsx"
$xl = new-object -ComObject Excel.Application
$workbook = $xl.Workbooks.Add()
$i = 0
$clusters = ('Hcohesity01','hcohesity03')
foreach ($vip in $clusters){
### create excel woorksheet
while($xlsx.Worksheets.Count -lt $i) { $xlsx.Worksheets.Add() }
#$worksheet = $workbook.Worksheets.Item($i)
$worksheet.Name = "$vip-Storage Growth"
$worksheet.activate()
### headings for data rows
$row = 1
$worksheet.Cells.Item($row,1) = 'Date'
$worksheet.Cells.Item($row,2) = 'Usage in Tib'
$row++
### authenticate
apiauth -vip $vip -username $username -domain corpads.local
### calculate startTimeMsecs
$startTimeMsecs = $(timeAgo $days days)/1000
### get cluster info
$clusterInfo = api get cluster?fetchStats=true
$clusterId = $clusterInfo.id
### collect $days of write throughput stats
#$stats = api get statistics/timeSeriesStats?schemaName=kBridgeClusterStats`&entityId=$clusterId`&metricName=kSystemUsageBytes`&startTimeMsecs=$startTimeMsecs`&rollupFunction=average`&rollupIntervalSecs=86400
$stats = api get "statistics/timeSeriesStats?endTimeMsecs=1662609600000&entityId=$clusterId&metricName=kMorphedUsageBytes&metricUnitType=0&range=day&rollupFunction=average&rollupIntervalSecs=86400&schemaName=kBridgeClusterStats&startTimeMsecs=$startTimeMsecs"
### populate excel worksheet with the throughput stats
foreach ($stat in $stats.dataPointVec){
$day = usecsToDate (($stat.timestampMsecs)*1000)
$consumed = $stat.data.int64Value/$TB
$worksheet.Cells.Item($row,1) = "$day".split()[0]
$worksheet.Cells.Item($row,2) = "{0:N2}" -f $consumed
$row++
}
### create excel chart
$chartData = $worksheet.Range("A1").CurrentRegion
$chart = $worksheet.Shapes.AddChart().Chart
$chart.chartType = 4
$chart.SetSourceData($chartData)
$chart.HasTitle = $true
$chart.ChartTitle.Text = "Storage Consumption Last $days Days"
$chart.Parent.Top = 50
$chart.Parent.Left = 150
$chart.Parent.Width = 600
$i ++
}
$xl.visible = $true
#[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xl)
$worksheet.columns.autofit() | Out-Null
$worksheet.usedRange.rows(1).Font.Bold = $True
$workbook.SaveAs($xlsx,51) | Out-Null

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

How do I replace all occurrences of string in Excel documents in a folder using Powershell

I was able to find here the code for Word Document files, how could I use /adjust the same set of code to run Excel files
Thanks
$objWord = New-Object -comobject Word.Application
$objWord.Visible = $false
$list = Get-ChildItem "C:\Users\john\foldername\*.*" -Include *.doc*
foreach($item in $list){
$objDoc = $objWord.Documents.Open($item.FullName,$true)
$objSelection = $objWord.Selection
$wdFindContinue = 1
$FindText = "1911"
$MatchCase = $False
$MatchWholeWord = $true
$MatchWildcards = $False
$MatchSoundsLike = $False
$MatchAllWordForms = $False
$Forward = $True
$Wrap = $wdFindContinue
$Format = $False
$wdReplaceNone = 0
$ReplaceWith = "456"
$wdFindContinue = 1
$ReplaceAll = 2
$a = $objSelection.Find.Execute($FindText,$MatchCase,$MatchWholeWord, `
$MatchWildcards,$MatchSoundsLike,$MatchAllWordForms,$Forward,`
$Wrap,$Format,$ReplaceWith,$ReplaceAll)
$objDoc.Save()
$objDoc.Close()
}
$objWord.Quit()
Based on this answer, you could do something like this:
$folderPath = "C:\Users\john\foldername\*"
$fileType = "*.xls*"
$excel = New-Object -ComObject Excel.Application
$textToReplace = #{
# "TextToFind" = "TextToReplaceWith"
"This1" = "That1"
"This2" = "That2"
"This3" = "That3"
}
Function findAndReplace($wsheet, $FindText, $ReplaceWith) {
#simple Replace to execute on all columns of a Worksheet object
$wsheet.Columns.Replace($FindText, $ReplaceWith) > $null
}
Function findAndReplaceMulti($wsheet, $lookupTable) {
#apply multiple Replace on the same Worksheet object
$lookupTable.GetEnumerator() | ForEach-Object {
findAndReplace $wsheet $_.Key $_.Value
}
}
Function findAndReplaceWholeWb($wbook, $lookupTable) {
#apply multiple Replace in all Worksheets
$wbook.Worksheets | ForEach-Object {
findAndReplaceMulti $_ $lookupTable
}
}
Get-ChildItem -Path $folderPath -Recurse -Filter $fileType | ForEach-Object {
$excel.Visible = $False
Write-Host "Processing `"$($_.Name)`"..."
$wbook = $excel.Workbooks.Open($_.FullName)
findAndReplaceWholeWb $wbook $textToReplace
$wbook.Close($True)
}
$excel.Quit()
$excel = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()

Excel (.xls file) - 4 sheets not possible?

need your help again!
This Script doesn't work. It works for the first 3 Sheets, but doesn't work for the last one. If I switch the itemnumber (eg. 3->4 and 4->3) the new 3 works and the new 4 does not. Is this some sort of bug? Or am I missing some commandlet to increase the "maximum sheet number"?
$Path = "C:\test.xls"
#Excelvar:
$Row = [int] 2
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $true
$Excel.DisplayAlerts = $false
#Sheets:
$ADUsers = "Active Directory Users"
$Groups = "Create Groups"
$UsertoGroup = "User to groups"
$DNS = "DNS"
#$Worksheet = $Workbook.Sheets.Add()
$checkxls = test-path -pathtype Any $Path
if ($checkxls -eq $false) {
$wb = $Excel.Workbooks.Add()
$ws1 = $wb.Worksheets.Item(1)
$ws1.Name = $ADUsers
$ws1.activate()
$ws2 = $wb.Worksheets.Item(2)
$ws2.Name = $Groups
$ws2.activate()
$ws3 = $wb.Worksheets.Item(3)
$ws3.Name = $UserToGroup
$ws3.activate()
$ws4 = $wb.Worksheets.Item(4)
$ws4.Name = $DNS
$ws4.activate()
$wb.SaveAs($Path)
$wb.Close()
$Excel.Quit()
Errorcode:
"Invalid Index. (Exception by HRESULT: 0x8002000B (DISP_E_BADINDEX))"
Thx for help in advance.
extra information:
using powershell 3.0
using excel 2010
I think it's because you're refering to a different workbook
this line
$wb = $Excel.Workbooks.Add()
implies you're working with a new workbook.
try adding
$wb.Worksheets.Add()
after the workbook is created, and see if that works.
$Path = "C:\test.xls"
#Excelvar:
$Row = [int] 2
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $true
$Excel.DisplayAlerts = $false
#Sheets:
$ADUsers = "Active Directory Users"
$Groups = "Create Groups"
$UsertoGroup = "User to groups"
$DNS = "DNS"
#$Worksheet = $Workbook.Sheets.Add()
$checkxls = test-path -pathtype Any $Path
if ($checkxls -eq $false) {
$wb = $Excel.Workbooks.Add()
$wb.Worksheets.add()
$ws1 = $wb.Worksheets.Item(1)
$ws1.Name = $ADUsers
$ws1.activate()
$ws2 = $wb.Worksheets.Item(2)
$ws2.Name = $Groups
$ws2.activate()
$ws3 = $wb.Worksheets.Item(3)
$ws3.Name = $UserToGroup
$ws3.activate()
$ws4 = $wb.Worksheets.Item(4)
$ws4.Name = $DNS
$ws4.activate()
$wb.SaveAs($Path)
$wb.Close()
$Excel.Quit()
Tried adding Sheet4 on excel and it is code is reading Sheet4 just fine
#Declare the file path and sheet name
$file = "C:\Documents\Folder\ExcelFile.xlsx"
$sheetName = "Sheet1"
#Create an instance of Excel.Application and Open Excel file
$objExcel = New-Object -ComObject Excel.Application
$workbook = $objExcel.Workbooks.Open($file)
$sheetCount = $workbook.Worksheets.Count
$sheet = $workbook.Worksheets.Item($sheetName)
$sheet4 = $workbook.Worksheets.Item("Sheet4")
Write-Host $sheetCount #sheet count is 4
$objExcel.Visible=$false
#Count max row
$rowMax = ($sheet.UsedRange.Rows).count
#Declare the starting positions
$rowName,$colName = 1,1
$rowAge,$colAge = 1,2
$rowCity,$colCity = 1,3
#loop to get values and store it
for ($i=1; $i -le $rowMax-1; $i++)
{
$name = $sheet.Cells.Item($rowName+$i,$colName).text
$age = $sheet.Cells.Item($rowAge+$i,$colAge).text
$city = $sheet.Cells.Item($rowCity+$i,$colCity).text
Write-Host ("My Name is: "+$name)
Write-Host ("My Age is: "+$age)
Write-Host ("I live in: "+$city)
}
#used $rowMax from Sheet1, you can declare a separate for Sheet4
for ($i=1; $i -le $rowMax-1; $i++)
{
$name = $sheet4.Cells.Item($rowName+$i,$colName).text
$age = $sheet4.Cells.Item($rowAge+$i,$colAge).text
$city = $sheet4.Cells.Item($rowCity+$i,$colCity).text
Write-Host ("My Name is: "+$name)
Write-Host ("My Age is: "+$age)
Write-Host ("I live in: "+$city)
}
#close excel file
$objExcel.quit()
Pardon my example, just a noob script :)

Resources