Powershell 2.0 write back to xlsx - excel

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.

Related

Excel / Copy paste 2 Columns into one using powershell

I'm really new in powershell and I try to write a script to help me in copying 2 or more columns and paste it into only one with a particular format.
What I want is something like that :
example
It's been 2 days I looked for the correct syntax and I can't find it
Thanks for your help !
Actually, I try to learn how excel and powershell works ( I started 1 week ago so I don't know anything )
I found a script to copy/paste some columns. I understand how it works but know I would like to paste these columns into only one ( like I said )
Here is the script I have :
$ExcelPath = ''
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $true
$WorkBook = $Excel.Workbooks.Open($ExcelPath)
$Worksheet = $Workbook.WorkSheets.item(“lolo”)
$Worksheet2 = $Workbook.WorkSheets.item(“lala”)
$worksheet.activate()
$lastRow1 = $worksheet.UsedRange.rows.count
$range1 = $worksheet.Range("A2:C$lastRow1")
$range1.copy()
$worksheet2.activate()
$lastRow2 = $worksheet2.UsedRange.rows.count + 1
$range2 = $worksheet2.Range("A$($lastRow2)")
$worksheet2.Paste($range2)
Convert your data to CSV by example. After it depends what you want to do, if you want to include null properties or not ...
Import-Csv -Path filepath | Select-Object -Properties *, #{"l"="Col4", e={ "`"$($_.Col1)`", `"$($_.Col2)`",`"$($_.Col3)`",`"`",`"`"" } } | Export-CSV -Path outputpath -Encoding UTF8 -NoTypeInformation

Powershell append data into existing XLSX with multiple sheets

New to Powershell and looking to learn.
Goal:
Trying to take the Data out of a .csv file (14 cells of data per row) and import into an existing .xlsx file Starting on the second row columns (A2:N2).
The .xlsx file has 4 sheets with the one I am looking to edit being labeled "Data". Data sheet/tab has 18 columns, the first 14 are where I would like the imported data starting on row (A2:N2-> End will vary).
Looking for a way to automate the report by filling rows A-N with data from a file (.csv) which gets generated automatically.
Sample of "Data" tab with some values:
Current process is to open one xls file and copy/past into cells starting at A2. Looking to automate this and have automated the report -> Emails .xls file, which I convert to .csv and remove some titles and extra info which is not needed using the following code:
Function ExcelCSV ($File)
{
$pwd = "C:\Users\..." #Removed local path
$excelFile = "$pwd\" + $File + ".xls"
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $false
$Excel.DisplayAlerts = $false
$wb = $Excel.Workbooks.Open($excelFile)
foreach ($ws in $wb.Worksheets)
{
$ws.SaveAs("$pwd\" + $File + ".csv", 6)
}
$Excel.Quit()
}
$TestFile = (Get-Content .\FileName.xls) -replace 'null',''
$TestFile | Out-File Test.xls
$FileName = "Test"
ExcelCSV -File $FileName
Get-Content Test.csv | Select-Object -Skip 2 | Select-Object -SkipLast 3 | Set-Content Test2.csv
Please use great ImportExcel powershell module ImportExcelModule
using it You can achieve Your goal by simply doing so
$csv=Import-CSV <YourImportParameters>
$csv|Export-Excel -Path $pwd -Show -StartRow 2 -StartColumn 2 -sheet $sheetname
Above will take the object and export it to excel file $pwd,sheet $sheetname starting from second row of second column
If You want to send that via mail to someone afterwards - Powershell can help You do that in 1 line too :)

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

Check header row of Excel sheet for particular column

I have over 150 excel files where some have an extra column (let's call it "ExtraColumn"), while some do not have this column. Instead of opening each file manually to see which ones have the extra column, I want to use powershell to figure it out.
The code I have tried so far hasn't seemed to have gotten me anywhere. If you have any suggestions or can point me to the correct answer, that would be very wonderful and much appreciated!
gci -Path C:\Test -Recurse | % {
$ExcelFile = (Get-Content $_.FullName -TotalCount 1)
if ($ExcelFile -like "ExtraColumn") {
Write-Host "$_ has the extra column"
} else {
Write-Host "$_ does not have the extra column"
}
}
You can use Excel ComObject, for the code simplicity just name the sheet otherwise you can find the sheet as well, add foreach section to run it on all files,
For the example i named the column - 'extracol'
$excel = New-Object -ComObject excel.application
$WB = $excel.Workbooks.Open('C:\exceltest.xlsx')
$WS = $Excel.WorkSheets.item("Sheet1")
$ExtraCol = ($ws.Columns.Find('extracol'))
if ($ExtraCol) {$ExtraCol.Delete()}
$wb.Save()
$wb.Close()
$excel.Quit()

Convert multiple xls to csv using powershell

I'm trying to convert multiple excel files (xls) to csv which is located in a folder using powershell.
I can convert a single file but need help converting multiple files in a folder.
But need advise on how to convert multiple files.
$ExcelWB = new-object -comobject excel.application
$Workbook = $ExcelWB.Workbooks.Open(c:\temp\temp.xls)
$Workbook.SaveAs("c:\temp\temp.csv",6)
$Workbook.Close($false)
$ExcelWB.quit()
You can just wrap it in a loop that iterates over all the files and change the xls extension to csv:
foreach($file in (Get-ChildItem "C:\temp")) {
$newname = $file.FullName -replace '\.xls$', '.csv'
$ExcelWB = new-object -comobject excel.application
$Workbook = $ExcelWB.Workbooks.Open($file.FullName)
$Workbook.SaveAs($newname,6)
$Workbook.Close($false)
$ExcelWB.quit()
}
There are caveats with this untested code but it should help wrap your head around your issue
$ExcelWB = new-object -comobject excel.application
Get-ChildItem -Path c:\folder -Filter "*.xls" | ForEach-Object{
$Workbook = $ExcelWB.Workbooks.Open($_.Fullname)
$newName = ($_.Fullname).Replace($_.Extension,".csv")
$Workbook.SaveAs($newName,6)
$Workbook.Close($false)
}
$ExcelWB.quit()
Take the lines in between the first and last and build a loop. Use Get-ChildItem to grab your xls files and then build a new name by replacing the extension if the FullName of the file
The conversion from xlsx files to csv can be done far quicker and without COM Objects - so without Excel installed - using the ImportExcel module developped by Doug Finke:
Install-Module -Name ImportExcel -RequiredVersion 5.4.2
gci *.xlsx | %{Import-Excel $_ | Export-Csv ($_.basename + ".csv")}
Or the other way around:
gci *.csv | %{Import-Csv $_ | Export-Excel ($_.basename + ".xlsx")}
Parameters available for the Import-Excel cmdlet:
WorksheetName
Specifies the name of the worksheet in the Excel workbook to import. By default, if no name is provided, the first worksheet will be imported.
DataOnly
Import only rows and columns that contain data, empty rows and empty columns are not imported.
HeaderName
Specifies custom property names to use, instead of the values defined in the column headers of the TopRow.
NoHeader
Automatically generate property names (P1, P2, P3, ..) instead of the ones defined in the column headers of the TopRow.
StartRow
The row from where we start to import data, all rows above the StartRow are disregarded. By default this is the first row.
EndRow
By default all rows up to the last cell in the sheet will be imported. If specified, import stops at this row.
StartColumn
The number of the first column to read data from (1 by default).
EndColumn
By default the import reads up to the last populated column, -EndColumn tells the import to stop at an earlier number.
Password
Accepts a string that will be used to open a password protected Excel file.
Expanding on the answer from #arco444, if you are doing this in bulk you should create the excel object outside the loop for a much more performant conversion
$ExcelWB = new-object -comobject excel.application
foreach($file in (Get-ChildItem "C:\temp")) {
$newname = $file.FullName -replace '\.xls$', '.csv'
$Workbook = $ExcelWB.Workbooks.Open($file.FullName)
$Workbook.SaveAs($newname,6)
$Workbook.Close($false)
}
$ExcelWB.quit()
Apologies I can't comment and edit queue has been full for some time, so posting as an answer instead.

Resources