Select Excel row --> Create Hashtable from Entries - excel

I have an Excel .xlsx-file which looks like this:
Now I'd like to create a PowerShell script, which can do the following:
Ask the User which row he wants to use (for example 4)
Create a hashtable with those entries
The hashtable should look like this:
Name Value
---- -----
Name Jane Doe
Age 67
Street Grace St. 19
Zipcode 12345
Date 03.03.2013
Does someone know how I can achieve this?
Plus: Is this actually achievable with a xlsx-File or do I need to use a CSV-file?

First, it is achievable if we convert the xls to csv:
$excelFilePath = "C:\Temp\abc.xlsx"
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $false
$Excel.DisplayAlerts = $false
$Workbook = $excel.Workbooks.Open($excelFilePath)
$($Workbook.Worksheets | where {$_.Name -eq "Sheet1"}).SaveAs("C:\Temp\abcd.csv", 6)
$Excel.Quit()
I would suggest using an ID instead of row number:
$importedAbcd = Import-Csv -Path "C:\Temp\abcd.csv" -Delimiter ";"
[int]$answer = Read-Host "Select ID to work with"
$object = $importedAbcd | Where-Object {$_.ID -eq $answer}
You have the row now converting it to hash:
$hash = #{}
$object.psobject.properties | Foreach { $hash[$_.Name] = $_.Value }

A more intuitive way is to
read the excel file directly with the Import-Excel module
pipe it to Out-Gridview, select a single line
and populate the hashtable
$HashTable = [ordered]#{}
Import-Excel "x:\path\sample.xls" | Out-GridView -OutputMode Single -Title "Select one line" |
ForEach-Object {$_.psobject.properties | ForEach-Object {
$HashTable[$_.Name]=$_.Value
}
}
$HashTable
Sample output:
> $HashTable
Name Value
---- -----
Name Molly Johnson
Agr 35
Street Murraay St. 86
Zipcode 54321
Date 02.02.2009

Related

How to get a list of open Excel workbooks in PowerShell

When I use PowerShell, I only get one (Workbook3) of several window titles (Workbook1, Workbook2, Workbook3), but I want to get the entire list of all open Excel books. I am trying to use the following code:
[array]$Titles = Get-Process | Where-Object {$_.mainWindowTItle} |Foreach-Object {$_.mainwindowtitle}
ForEach ($item in $Titles){Write-Host $item}
UPD. (We get a list of books, but we don't see which ones only to read)
If I open the book in read-only mode, it will not be visible in the output of the program. In the Task Manager this mark is in the name of the window.
$excel = [Runtime.Interopservices.Marshal]::
GetActiveObject('Excel.Application')
ForEach ( $wkb in $excel.workbooks ) {
$wkb.Name
}
This seems to do the trick:
Clear-Host
$excel = [Runtime.Interopservices.Marshal]::
GetActiveObject('Excel.Application')
ForEach ( $wkb in $excel.workbooks ) {
$wkb.Name
}
Sample Output:
PERSONAL.xlsm
Cash Count.xls
Check Calc.xls
Coke Price Comparison Sheet.xls
PS>
HTH
So, my solution:
Clear-Host
Remove-Variable * -ErrorAction SilentlyContinue
$excel = [Runtime.Interopservices.Marshal]::GetActiveObject('Excel.Application')
$date = Get-Date -Format "d.MM.y HH:mm"
$person = $env:UserName
#arrays with books and status
ForEach ( $book in $excel.workbooks ) {$books_names = ,$book.Name + $books_names}
ForEach ( $book in $excel.workbooks ) {$books_reads = ,$book.ReadOnly + $books_reads}
#print
#ForEach ($item in $books_names){$item}
#ForEach ($item in $books_reads){$item}
#delete empty string
$books_names = $books_names[0..($books_names.Count-2)]
$books_reads = $books_reads[0..($books_reads.Count-2)]
#replace
$books_reads = $books_reads -replace "False" , "write."
$books_reads = $books_reads -replace "True" , "read"
# Table view
$t = $books_names |%{$i=0}{[PSCustomObject]#{person= $person; date= $date;book=$_; status=$books_reads[$i]};$i++}
#$t | ft
#$t | Out-GridView
# write to txt
Add-Content "C:\My\1.txt" $t | ft
#pause
1.txt looks like:
#{person=BelyaevKN; date=14.06.21 14:05; book=workbook1; status=write}
#{person=BelyaevKN; date=14.06.21 14:05; book=workbook2; status=read}

Delete extra columns of table before exporting to Excel

I have a PowerShell script that runs a few API request and then export information on the test into an excel.
When I create the table to I adds couple column for the results. However when I export the tables via Excel there are a bunch of extra column I don't want.
$ResultsTable = New-Object System.Data.DataTable "ResultsTable"
$RTC1 = New-Object system.Data.DataColumn "Type",([string])
$RTC2 = New-Object system.Data.DataColumn "Endpoint",([string])
$RTC3 = New-Object system.Data.DataColumn "PassRate",([string])
$RTC4 = New-Object system.Data.DataColumn "AvgTime",([string])
$RTC5 = New-Object system.Data.DataColumn "MaxTime",([string])
$RTC6 = New-Object system.Data.DataColumn "AvgSize",([string])
$RTC7 = New-Object system.Data.DataColumn "MaxSize",([string])
$ResultsTable.Columns.Add($RTC1)
$ResultsTable.Columns.Add($RTC2)
$ResultsTable.Columns.Add($RTC3)
$ResultsTable.Columns.Add($RTC4)
$ResultsTable.Columns.Add($RTC5)
$ResultsTable.Columns.Add($RTC6)
$ResultsTable.Columns.Add($RTC7)
$Row = $ResultsTable.NewRow()
$Row.Type = "Direct"
$Row.Endpoint = $Uri
$Row.PassRate = "$PassRate%"
$Row.AvgTime = $AvgTime
$Row.MaxTime = $MaxTime
$Row.AvgSize = $AvgSize
$Row.MaxTime = $MaxSize
$ResultsTable.Rows.Add($Row)
$ResultsTable | Export-Excel -Path ".\Output\Unit\API.Customer.Unit.Tests - $DateTime.xlsx" `
-AutoSize `
-WorksheetName "Results" `
-Title "Results Table" `
-TitleBold `
-BoldTopRow `
-FreezeTopRow
The output of this export looks like:
I only need the Columns A - G. How do I get rid of the other columns?
Either select the columns you want to keep:
$ResultsTable |
Select-Object Type, Endpoint, PassRate, AvgTime, MaxTime, AvgSize, MaxSize |
Export-Excel ...
or remove the columns you don't want to keep:
$ResultsTable |
Select-Object -Property * -Exclude RowError, RowState, Table, ItemArray, HasErrors |
Export-Excel ...
If you know that you're always going to need exactly the columns defined in the table, you could also reference them directly:
$ResultsTable |
Select-Object -Property $ResultsTable.Columns.ColumnName |
Export-Excel ...

Excel File Reader Script in Powershell Not Working?

I wanted to read the contents of an excel file and then output them to a text file. Currently, my code only outputs the results to the console.
I tried to write code that gets the applicable columns and then loads them into an array to be written to the console later.
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $False
$PLACE = "C:\Somepath\somename.xlsx"
$OpenFile = $Excel.Workbooks.Open($PLACE)
$Workbook = $OpenFile.Worksheets
$Worksheet = $Workbook.Item(1)
# Get the values for each column
$MNumber = $Worksheet.Cells | where {$_.value2 -eq "Material-Number"} | select -First 1
$MDescription = $Worksheet.Cells | where {$_.value2 -eq "Material-Description"} | select -First 1
# Get the values for each row in Material Number
$NumValues = #()
$NumValues = for($i=2; $MNumber.Cells.Item($i).Value2 -ne $null; $i++ ){
$MNumber.Cells.Item($i)
}
# Get the values for each row in Material Description
$DescValues = #()
$DescValues = for($i=2; $MDescription.Cells.Item($i).Value2 -ne $null; $i++ ){
$MDescription.Cells.Item($i)
}
$NumValues | ForEach-Object {Write-host $_.value2}
$DescValues | ForEach-Object {Write-Host $_.value2}
Nothing output to the console by the end of the process. Even though those are the exact columns and each column contains data.
Try match instead of eq here:
$MNumber = $Worksheet.Cells | where {$_.value2 -match "Material-Number"} | select -First 1
$MDescription = $Worksheet.Cells | where {$_.value2 -match "Material-Description"} | select -First 1
I'm not sure if your cell has other strings within it on $_.value2 but if it does eq will not work. It's trying to find the ENTIRE string it's conditioning against. Therefore, if there's other strings of data within the cell it will not pull the data.

CSV Not Exporting after merge

I asked a similar question here and it got me on a very good track, however while trying to 'all in one' it, I ended up with an issue. I'm very close, but after testing numerous theories I'm starting to reverse what I need.
I have a multiple sheet Excel - I need to pull the first sheet, CSV-ify it, then remove the headers, then only get the first 3 digits from the first line.("List Name")
My PS is:
#Create and get my Excel Obj
$excel = New-Object -comobject Excel.Application
$excel.visible=$false
$excel.DisplayAlerts=$false
$UserWorkBook = $excel.Workbooks.Open("C:\path\to\file\CompleteTest.xls")
$UserWorksheet = $UserWorkBook.Worksheets.Item(1)
$hitlistCSV = "C:\path\to\file\BroadcastReport.csv"
$xlCSV = 6 #changed-grav
#Save, close, and clean up
$UserWorksheet.SaveAs($hitlistCSV,$xlCSV)
$UserWorkBook.close()
$excel.quit()
$excel = $null
$expandfile = "C:\path\to\file\BroadcastReport.csv"
(gc $expandfile | select -Skip 1) | sc $expandfile
Import-Csv $expandfile | ForEach-Object {
$_."List Name" = $_."List Name".SubString(0,3)
$_
}
$expandfile | Export-Csv "C:\path\to\file\BroadcastReport.csv" -NoTypeInformation
I've modified it a lot, and tried to make it single runnable to avoid schedule multiple BATs. Everything seems to work - It pulls the first sheet, renames it, and replaces the top line EVERY time I run the 2nd portion. However, the SubString portion isn't taking effect, nor am I getting a 'FileExport' file.
My last modification now makes it so everytime it replies that I need to 'supply an Input Object' so I feel like it has to do with function-izing it. For reference the line I need to split is double quoted because it has commas. Ex:
"123|ST,City"
I only need the 123. When I just import the file and run my ForEach function, the output is what I expect. Just saving it to the file isn't.
Thanks.
Welcome any input.
XLS Source
This is the header line I need to skip
List Name Broadcast Name ColumC ColumD Colum E ColumF Colum G ColumJ
401|ST, City ST, City - More Text(LSM) (16803) 1 854 73 854 233 27.28%
402|ST, City ST, City - October (LSM) (16807) 1 851 57 851 186 21.86%
CSV Source after XLS -> CSV
One thing that is weird is that once it becomes a CSV, I'm left with ~6 fields of just commas. Example:
List Name,Broadcast Name,ColumC,ColumD,Colum E ,Colum,Colum F,ColumG,,,,,,
"402|ST,City","ST, City - More Text(ACR) (16803)",1,854,73,854,233,27.28%,,,,,,
"402|ST,City","City, ST - Text (LSM) (16807)",1,851,57,851,186,21.86%,,,,,,
UPDATE:
Added example, updated source - Column names keep a space where a space exists.
Broadcast Name column -does- have a comma in it, but I assume it's being exported with double quotes.
RESOLUTION
#Create and get my Excel Obj
$excel = New-Object -comobject Excel.Application
$excel.visible=$false
$excel.DisplayAlerts=$false
$UserWorkBook = $excel.Workbooks.Open("C:\path\to\file\ExcelBook.xls")
$UserWorksheet = $UserWorkBook.Worksheets.Item(1)
$hitlistCSV = "C:\path\to\file\Output.csv"
$xlCSV = 6 #changed-grav
#Save, close, and clean up
$UserWorksheet.SaveAs($hitlistCSV,$xlCSV)
$UserWorkBook.close()
$excel.quit()
$excel = $null
$expandfile = "C:\path\to\file\Output.csv"
$report = get-content $ExpandFile | select -skip 1 | convertfrom-csv | ForEach-Object {
$_."List Name" = $_."List Name".SubString(0,3)
$_
}
$report | Export-Csv ""C:\path\to\file\Output.csv" -force -NoTypeInformation
Issue was a slight modification to the export function being separated functions. I assume due to some sort of write lock.
The problem is on your last line, you are setting $expandfile to the return result of export-csv, it is prompting you to provide an input because it is trying to perform the export but is only provided with a path. Just change the last line to
$expandfile | Export-Csv "C:\Users\Donavin\Desktop\TXRH\FileExport.csv" -NoTypeInformation
EDIT
Ok need to make a few more changes for things to work correctly, valid import/export code is below
$expandfile = "C:\path\to\file\BroadcastReport.csv"
(gc $expandfile | select -Skip 1) | sc $expandfile
Import-Csv $expandfile | ForEach-Object {
$_."List Name" = $_."List Name".SubString(0,3)
$_
} | Export-Csv "C:\path\to\file\BroadcastReport.csv" -force -NoTypeInformation

Powershell 2.0 write back to xlsx

Cross post from powershell.org..
I am trying to have Powershell read an xlsx for username info, convert to a csv (to be imported) and then write back something to the xlsx so next time it won't reimport the same users.
I don't want to delete the users in the xlsx but am thinking to add a date column or some other data maybe the word "created" and have powershell write this data in an available column. But then I would have to have my script ignore this new column if contains a old date or the word created?
<br> Current xlsx columns headers
<br> A B C
<br> 1 Full Name, Personal Email, "write back data"
<br> 2 John Doe Jdoe#gmail.com, Created (Sample write back data)
<br> 3 Don Juan Djuan#gmail.com, Date Imported (sample write back data)
Convert to csv code (This part is working fine.)
$File = "C:\Scripts\Excel\Accounts.xlsx"
$Savepath1 = "C:\Scripts\Csv\Employee Accounts.csv"
$SheetName1 = "Employee Accounts"
$ObjExcel = New-Object -ComObject Excel.Application
$Objexcel.Visible = $false
$Objworkbook=$ObjExcel.Workbooks.Open($File)
$Objworksheet=$Objworkbook.worksheets.item($Sheetname1)
$Objworksheet.Activate()
$Objexcel.application.DisplayAlerts= $False
$Objworkbook.SaveAs($SavePath1,6)
$Objworkbook.Close($True)
$ObjExcel.Quit()
Here is my current import-csv code
$EmployeeAccounts = Import-Csv $savepath1 | Where-Object { $_.Fullname -and $_.PersonalEmail}
Things to consider:
There might be additional concatenated info in additional fields added to the xlsx. Therefore excel might count these as used rows if the fields have formulas in them. So I only want to write the data to the new column if there is a username and email address in columns A & B.
Thanks!
To be honest it's going to be simpler to import the whole thing, perform your process filtering for entries that don't have anything in the Updated field, update the "Updated" field for each entry that you processed, and then just write the entire thing back to the file. So, something like:
$Users = Import-CSV $FilePath
$Users | ?{[String]::IsNullOrEmpty($_.Updated)} | %{
Do stuff here
$_.Updated = Get-Date
}
$Users | Export-CSV $FilePath -Force -NoTypeInfo
Edit: Well then, that does complicate things a little bit. So, this one does take a plugin, but it's a plugin that I whole heartedly feel should be included in almost any installation that's functionally used regularly IMHO. The PowerShell Community Extensions (PSCX) can be gotten from pscx.codeplex.com and will grant you access to the command Out-Clipboard which is awesome for what you want to do.
$Users = Import-CSV $FilePath
$Users | ?{[String]::IsNullOrEmpty($_.Updated)} | %{
Do stuff here
$_.Updated = Get-Date
}
$Users | Select Updated | ConvertTo-CSV -Delimiter "`t" -NoTypeInformation | Out-Clipboard
$SheetName1 = "Employee Accounts"
$ObjExcel = New-Object -ComObject Excel.Application
$Objexcel.Visible = $false
$Objworkbook=$ObjExcel.Workbooks.Open($File)
$Objworksheet=$Objworkbook.worksheets.item($Sheetname1)
$Objworksheet.Activate()
$Range = $Objworksheet.Range("C1","C1")
$Objworksheet.Paste($Range, $false)
$Objexcel.DisplayAlerts = $false
$Objworkbook.Save()
$Objexcel.DisplayAlerts = $true
$Objworkbook.Close()
$Objexcel.Quit()
[void][System.Runtime.Interopservices.Marshal]::FinalReleaseComObject($Objexcel)
That will paste your data, header included, into column C.

Resources