Compare commits

..

54 Commits

Author SHA1 Message Date
dfinke
d1c3e7b23e Tweaked 2017-10-03 10:00:49 -04:00
dfinke
f0203f38e8 Bumped the version 2017-10-03 10:00:40 -04:00
Doug Finke
ef816d28d2 Merge pull request #228 from jeremytbrun/TitleFix
Title fix (Issues #182 and #89)
2017-10-03 09:54:49 -04:00
Brun
85512c2261 README update 2017-10-03 08:39:09 -04:00
Brun
409f69e915 Updated README 2017-10-03 08:29:10 -04:00
Brun
58ae6845ff Updated README 2017-10-03 08:28:37 -04:00
Brun
5d70003582 Title fixes 2017-10-02 10:04:13 -04:00
Doug Finke
9a66cb6123 Merge pull request #227 from DarkLite1/ImportExcelPassword
Import excel added parameter 'Password'
2017-09-29 20:53:01 -04:00
DarkLite1
efb9e158b5 Added help text 2017-09-28 14:21:55 +02:00
DarkLite1
75aaff300a Correct version nr 2017-09-28 14:14:36 +02:00
DarkLite1
12fd17b9ea Bumped version to 4.0.1
- Renamed 'TopRow' to 'StartRow' and added alias
  (More concise with future parameter names like 'StartColumn')
- Removed 'ChangeList' in 'Notes' as this is tracked in Git
- Added parameter 'Password' to import password protected files
- Added Pester tests for parameter aliasses and:
	- parameter 'Password'
	- parameter 'Path' validation for extensions '.xls' and '.xlsx'
	- 'HeaderName' witb blanks
- Changed comments in Pester tests from '<# Comment #>' to '# Comment'
  (Easier to outcomment a whole block of tests when performing a test on a specific piece of code)
2017-09-28 14:12:07 +02:00
DarkLite1
facb38a2aa Merge branch 'master' of https://github.com/dfinke/ImportExcel 2017-09-27 15:51:51 +02:00
Doug Finke
222e0609d9 Merge pull request #217 from DarkLite1/ImportExcelFirstWorksheet
Import-Excel parameter
2017-09-22 16:07:34 -04:00
Brun
a3c2a92e33 Header formatting fix when Title is used (Issue #182) 2017-09-20 13:56:06 -04:00
DarkLite1
1f435277a5 Merge branch 'ImportExcelFirstWorksheet' 2017-09-20 09:40:00 +02:00
DarkLite1
295b369a81 Improved paramter validation and fixed 'WorksheetName' position. Extra Pester tests added too. 2017-09-20 09:35:57 +02:00
Doug Finke
84ad62989a Merge pull request #216 from DarkLite1/ImportExcelFirstWorksheet
Import excel first worksheet
2017-09-19 11:36:23 -04:00
DarkLite1
a0563d4daa Import-Excel updated help 2017-09-19 16:00:11 +02:00
DarkLite1
2c16cdcbfe Import-Excel select first worksheet by default 2017-09-19 15:55:13 +02:00
DarkLite1
e45437e32e Fixed spelling 2017-09-18 11:50:30 +02:00
dfinke
aadae64105 Updated to 4.0 2017-09-12 19:22:31 -04:00
Doug Finke
41b2455705 Merge pull request #202 from DarkLite1/master
Rewrote 'Import-Excel' to fix some serious bugs and added function 'Update-FirstObjectProperties'
2017-09-12 19:13:32 -04:00
DarkLite1
d75350c659 Improved tests for emtpy rows after TopRow x 2017-09-11 14:06:45 +02:00
DarkLite1
323c52a24b Added alias HeaderRow for TopRow to ImportExcel 2017-09-08 12:53:23 +02:00
DarkLite1
bcc2db8657 Improved ImportExcel.Tests 2017-09-07 13:38:43 +02:00
DarkLite1
e42fa83043 Added position to params of 'Import-Excel' 2017-08-22 08:48:20 +02:00
DarkLite1
662d5913ae Rewrote 'Import-Excel':
- Added parameter sets for proper parameter validation and to make sure '-NoHeader' and '-HeaderRow' aren't used together
- Added try/catch clause, CmdLetBinding and verbose messages
- Renamed 'HeaderRow' to 'TopRow' to avoid confusion with other parameters
- Renamed '-Header' to '-HeaderName'
- Added test for duplicate property names
- Added test for empty worksheet
- Added test for no data after TopRow
- Fixed incorrect import when there's no value in the first column
- Fixed values being imported under the wrong property name in case one
- Fixed incorrect import in case column A is empty and B and C not ( '$Worksheet.Dimension.Columns' is unreliable because it will say 2 columns are in use while it should say 3).
(Ex. Add data in cell B2 and C2, use the '-NoHeader' switch, notice P1 and P2 are incorrectly blanc.)
2017-08-21 15:34:30 +02:00
DarkLite1
d8d624ba9c Added the function 'Update-FirstObjectProperties'
Added help text in 'Export-Excel'
Added try/catch to 'Install' and 'InstallModule'
Improved code readability in 'Install' and 'InstallModule'
2017-07-26 13:37:08 +02:00
DarkLite1
2e7df0a2fe Merge remote-tracking branch 'upstream/master' 2017-07-25 14:07:32 +02:00
dfinke
b90087bd63 Updated to include ConvertTo-ExcelXlsx 2017-07-03 13:44:01 -04:00
Doug Finke
c39a012205 Merge pull request #195 from NordbergKMD/master
Update and rename ConvertTo-ExcelXlsx.ps1 to ConvertToExcelXlsx.ps1
2017-06-29 16:16:10 -04:00
Mikkel Nordberg
675ba9d664 Update and rename ConvertTo-ExcelXlsx.ps1 to ConvertToExcelXlsx.ps1 2017-06-29 16:19:07 +02:00
Doug Finke
a8b20df16c Merge pull request #192 from NordbergKMD/master
ConvertTo-ExcelXlsx
2017-06-29 08:41:55 -04:00
Mikkel Nordberg
097f11a661 Minor patch
Consolidated parameter validation.
Added error handling on -Force if destination file cannot be deleted.
2017-06-29 14:18:29 +02:00
Mikkel Nordberg
c359560fd8 Removed case-sesitivity check.
-ne comparison operator is case insensitive so there is no need for .ToLower()
2017-06-29 12:23:24 +02:00
Mikkel Nordberg
ea0a5a7c76 Update and rename Convert-XLSToXLSX.ps1 to ConvertTo-ExcelXlsx.ps1 2017-06-27 13:20:38 +02:00
DarkLite1
39019d2680 Merge remote-tracking branch 'refs/remotes/origin/master' 2017-06-26 13:15:21 +02:00
DarkLite1
d009581b1b Merge remote-tracking branch 'refs/remotes/origin/master' into dfinke/master 2017-06-26 13:13:23 +02:00
Mikkel Nordberg
8344118c11 Rename Convert-XLSToXLSX to Convert-XLSToXLSX.ps1 2017-06-23 15:56:10 +02:00
Mikkel Nordberg
1b695478e7 Create Convert-XLSToXLSX 2017-06-23 15:55:06 +02:00
dfinke
39d176e31b Updated 2017-06-15 19:56:43 -04:00
dfinke
ed84db6b2e tweaked 2017-06-15 19:56:36 -04:00
dfinke
b33223460e Added example 2017-06-15 19:43:54 -04:00
dfinke
822e63a667 Updated for copying 2017-06-15 19:42:51 -04:00
Doug Finke
f9aa52cdae Merge pull request #190 from dfinke/CodeGen-SQL
Code gen sql
2017-06-15 19:35:40 -04:00
dfinke
615f677b2e Added convert from Excel 2017-06-15 19:34:04 -04:00
dfinke
26f6df7168 Added Export-ExcelAsSQLInsert 2017-06-14 22:44:11 -04:00
Doug Finke
dc67012590 Merge pull request #187 from DarkLite1/master
Major update to 'Export-Excel'
2017-06-10 10:54:33 -04:00
DarkLite1
2ae32dae7c Merge pull request #2 from DarkLite1/ImportExcel
Snall fixe verbose
2017-05-19 14:54:31 +02:00
DarkLite1
e8a027c951 Snall fixe verbose
Verbose for the header was not in the right place
2017-05-19 14:49:14 +02:00
DarkLite1
363deae40d Merge pull request #1 from DarkLite1/ImportExcel
Small fix for ConvertTo-Number
2017-05-16 12:43:28 +02:00
DarkLite1
1cfa5c2115 Small fix for ConvertTo-Number
Forgot to initialize the variable
2017-05-16 12:41:00 +02:00
DarkLite1
2354636edd Major update
Added parameter ‘NoNumberConversion'
- Allows us to pass on data to Excel without trying to parse it as a
number
Some code clean-up:
- Removed repeating code by using functions
- Some syntax clean-up for better readability
- More clear verbose messages
- Improved error handling
Improved help
- Added parameter explanations
- Added more examples
2017-05-15 14:55:04 +02:00
DarkLite1
81fa60dad8 Merge remote-tracking branch 'refs/remotes/origin/master' into dfinke/master 2017-02-23 08:59:39 +01:00
13 changed files with 3458 additions and 456 deletions

50
ConvertFromExcelData.ps1 Normal file
View File

@@ -0,0 +1,50 @@
function ConvertFrom-ExcelData {
<#
.SYNOPSIS
Reads data from a sheet, and for each row, calls a custom scriptblock with a list of property names and the row of data.
.EXAMPLE
ConvertFrom-ExcelData .\testSQLGen.xlsx {
param($propertyNames, $record)
$reportRecord = @()
foreach ($pn in $propertyNames) {
$reportRecord += "{0}: {1}" -f $pn, $record.$pn
}
$reportRecord +=""
$reportRecord -join "`r`n"
}
First: John
Last: Doe
The Zip: 12345
....
#>
param(
[Alias("FullName")]
[Parameter(ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true)]
[ValidateScript( { Test-Path $_ -PathType Leaf })]
$Path,
[ScriptBlock]$scriptBlock,
[Alias("Sheet")]
$WorkSheetname = 1,
[int]$HeaderRow = 1,
[string[]]$Header,
[switch]$NoHeader,
[switch]$DataOnly
)
$null = $PSBoundParameters.Remove('scriptBlock')
$params = @{} + $PSBoundParameters
$data = Import-Excel @params
$PropertyNames = $data[0].psobject.Properties |
Where-Object {$_.membertype -match 'property'} |
Select-Object -ExpandProperty name
foreach ($record in $data) {
& $scriptBlock $PropertyNames $record
}
}

View File

@@ -0,0 +1,46 @@
function ConvertFrom-ExcelToSQLInsert {
param(
[Parameter(Mandatory = $true)]
$TableName,
[Alias("FullName")]
[Parameter(ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true)]
[ValidateScript( { Test-Path $_ -PathType Leaf })]
$Path,
[Alias("Sheet")]
$WorkSheetname = 1,
[int]$HeaderRow = 1,
[string[]]$Header,
[switch]$NoHeader,
[switch]$DataOnly
)
$null = $PSBoundParameters.Remove('TableName')
$params = @{} + $PSBoundParameters
ConvertFrom-ExcelData @params {
param($propertyNames, $record)
$ColumnNames = "'" + ($PropertyNames -join "', '") + "'"
$values = foreach ($propertyName in $PropertyNames) { $record.$propertyName }
$targetValues = "'" + ($values -join "', '") + "'"
"INSERT INTO {0} ({1}) Values({2});" -f $TableName, $ColumnNames, $targetValues
}
# $data = Import-Excel @params
# $PropertyNames = $data[0].psobject.Properties |
# Where-Object {$_.membertype -match 'property'} |
# Select-Object -ExpandProperty name
# $ColumnNames = "'" + ($PropertyNames -join "', '") + "'"
# foreach ($record in $data) {
# $values = $(foreach ($propertyName in $PropertyNames) {
# $record.$propertyName
# })
# $targetValues = "'" + ($values -join "', '") + "'"
# "INSERT INTO {0} ({1}) Values({2});" -f $TableName, $ColumnNames, $targetValues
# }
}

53
ConvertToExcelXlsx.ps1 Normal file
View File

@@ -0,0 +1,53 @@
Function ConvertTo-ExcelXlsx {
[CmdletBinding()]
Param
(
[parameter(Mandatory=$true, ValueFromPipeline)]
[string]$Path,
[parameter(Mandatory=$false)]
[switch]$Force
)
Process
{
if(-Not ($Path | Test-Path) ){
throw "File not found"
}
if(-Not ($Path | Test-Path -PathType Leaf) ){
throw "Folder paths are not allowed"
}
$xlFixedFormat = 51 #Constant for XLSX Workbook
$xlsFile = Get-Item -Path $Path
$xlsxPath = "{0}x" -f $xlsFile.FullName
if($xlsFile.Extension -ne ".xls"){
throw "Expected .xls extension"
}
if(Test-Path -Path $xlsxPath){
if($Force){
try {
Remove-Item $xlsxPath -Force
} catch {
throw "{0} already exists and cannot be removed. The file may be locked by another application." -f $xlsxPath
}
Write-Verbose $("Removed {0}" -f $xlsxPath)
} else {
throw "{0} already exists!" -f $xlsxPath
}
}
try{
$Excel = New-Object -ComObject "Excel.Application"
} catch {
throw "Could not create Excel.Application ComObject. Please verify that Excel is installed."
}
$Excel.Visible = $false
$Excel.Workbooks.Open($xlsFile.FullName) | Out-Null
$Excel.ActiveWorkbook.SaveAs($xlsxPath, $xlFixedFormat)
$Excel.ActiveWorkbook.Close()
$Excel.Quit()
}
}

View File

@@ -0,0 +1,13 @@
ConvertFrom-ExcelToSQLInsert People .\testSQLGen.xlsx
ConvertFrom-ExcelData .\testSQLGen.xlsx {
param($propertyNames, $record)
$reportRecord = @()
foreach ($pn in $propertyNames) {
$reportRecord += "{0}: {1}" -f $pn, $record.$pn
}
$reportRecord +=""
$reportRecord -join "`r`n"
}

Binary file not shown.

View File

@@ -1,13 +1,185 @@
Function Export-Excel {
<#
.SYNOPSIS
Export data to an Excel work sheet.
Export data to an Excel worksheet.
.DESCRIPTION
Export data to an Excel file and where possible try to convert numbers so Excel recognizes them as numbers instead of text. After all. Excel is a spreadsheet program used for number manipulation and calculations. In case the number conversion is not desired, use the parameter '-NoNumberConversion *'.
.PARAMETER ConditionalText
Applies a 'Conditional formatting rule' in Excel on all the cells. When specific conditions are met a rule is triggered.
.PARAMETER NoNumberConversion
By default we convert all values to numbers if possible, but this isn't always desirable. NoNumberConversion allows you to add exceptions for the conversion. Wildcards (like '*') are allowed.
.PARAMETER AutoFilter
Enables the 'Filter' in Excel on the complete header row. So users can easily sort, filter and/or search the data in the select column from within Excel.
.PARAMETER AutoSize
Sizes the width of the Excel column to the maximum width needed to display all the containing data in that cell.
.PARAMETER Now
The 'Now' switch is a shortcut that creates automatically a temporary file, enables 'AutoSize', 'AutoFiler' and 'Show', and opens the file immediately.
.PARAMETER NumberFormat
Formats all values that can be converted to a number to the format specified.
Examples:
# integer (not really needed unless you need to round numbers, Excel with use default cell properties)
'0'
# integer without displaying the number 0 in the cell
'#'
# number with 1 decimal place
'0.0'
# number with 2 decimal places
'0.00'
# number with 2 decimal places and thousand separator
'#,##0.00'
# number with 2 decimal places and thousand separator and money symbol
'€#,##0.00'
# percentage (1 = 100%, 0.01 = 1%)
'0%'
# Blue color for positive numbers and a red color for negative numbers. All numbers will proceed a dollar sign '$'.
'[Blue]$#,##0.00;[Red]-$#,##0.00'
.PARAMETER Show
Opens the Excel file immediately after creation. Convenient for viewing the results instantly without having to search for the file first.
.EXAMPLE
Get-Service | Export-Excel .\test.xlsx
Get-Process | Export-Excel .\Test.xlsx -show
Export all the processes to the Excel file 'Test.xlsx' and open the file immediately.
.EXAMPLE
Get-Process | Export-Excel .\test.xlsx -show\
$ExcelParams = @{
Path = $env:TEMP + '\Excel.xlsx'
Show = $true
Verbose = $true
}
Remove-Item -Path $ExcelParams.Path -Force -EA Ignore
Write-Output -1 668 34 777 860 -0.5 119 -0.1 234 788 |
Export-Excel @ExcelParams -NumberFormat '[Blue]$#,##0.00;[Red]-$#,##0.00'
Exports all data to the Excel file 'Excel.xslx' and colors the negative values in 'Red' and the positive values in 'Blue'. It will also add a dollar sign '$' in front of the rounded numbers to two decimal characters behind the comma.
.EXAMPLE
$ExcelParams = @{
Path = $env:TEMP + '\Excel.xlsx'
Show = $true
Verbose = $true
}
Remove-Item -Path $ExcelParams.Path -Force -EA Ignore
[PSCustOmobject][Ordered]@{
Date = Get-Date
Formula1 = '=SUM(F2:G2)'
String1 = 'My String'
String2 = 'a'
IPAddress = '10.10.25.5'
Number1 = '07670'
Number2 = '0,26'
Number3 = '1.555,83'
Number4 = '1.2'
Number5 = '-31'
PhoneNr1 = '+32 44'
PhoneNr2 = '+32 4 4444 444'
PhoneNr3 = '+3244444444'
} | Export-Excel @ExcelParams -NoNumberConversion IPAddress, Number1
Exports all data to the Excel file 'Excel.xslx' and tries to convert all values to numbers where possible except for 'IPAddress' and 'Number1'. These are stored in the sheet 'as is', without being converted to a number.
.EXAMPLE
$ExcelParams = @{
Path = $env:TEMP + '\Excel.xlsx'
Show = $true
Verbose = $true
}
Remove-Item -Path $ExcelParams.Path -Force -EA Ignore
[PSCustOmobject][Ordered]@{
Date = Get-Date
Formula1 = '=SUM(F2:G2)'
String1 = 'My String'
String2 = 'a'
IPAddress = '10.10.25.5'
Number1 = '07670'
Number2 = '0,26'
Number3 = '1.555,83'
Number4 = '1.2'
Number5 = '-31'
PhoneNr1 = '+32 44'
PhoneNr2 = '+32 4 4444 444'
PhoneNr3 = '+3244444444'
} | Export-Excel @ExcelParams -NoNumberConversion *
Exports all data to the Excel file 'Excel.xslx' as is, no number conversion will take place. This means that Excel will show the exact same data that you handed over to the 'Export-Excel' function.
.EXAMPLE
$ExcelParams = @{
Path = $env:TEMP + '\Excel.xlsx'
Show = $true
Verbose = $true
}
Remove-Item -Path $ExcelParams.Path -Force -EA Ignore
Write-Output 489 668 299 777 860 151 119 497 234 788 |
Export-Excel @ExcelParams -ConditionalText $(
New-ConditionalText -ConditionalType GreaterThan 525 -ConditionalTextColor DarkRed -BackgroundColor LightPink
)
Exports data that will have a 'Conditional formatting rule' in Excel on these cells that will show the background fill color in 'LightPink' and the text color in 'DarkRed' when the value is greater then '525'. In case this condition is not met the color will be the default, black text on a white background.
.EXAMPLE
$ExcelParams = @{
Path = $env:TEMP + '\Excel.xlsx'
Show = $true
Verbose = $true
}
Remove-Item -Path $ExcelParams.Path -Force -EA Ignore
Get-Service | Select Name, Status, DisplayName, ServiceName |
Export-Excel @ExcelParams -ConditionalText $(
New-ConditionalText Stop DarkRed LightPink
New-ConditionalText Running Blue Cyan
)
Export all services to an Excel sheet where all cells have a 'Conditional formatting rule' in Excel that will show the background fill color in 'LightPink' and the text color in 'DarkRed' when the value contains the word 'Stop'. If the value contains the word 'Running' it will have a background fill color in 'Cyan' and a text color 'Blue'. In case none of these conditions are met the color will be the default, black text on a white background.
.EXAMPLE
$ExcelParams = @{
Path = $env:TEMP + '\Excel.xlsx'
Show = $true
Verbose = $true
}
Remove-Item -Path $ExcelParams.Path -Force -EA Ignore
$Array = @()
$Obj1 = [PSCustomObject]@{
Member1 = 'First'
Member2 = 'Second'
}
$Obj2 = [PSCustomObject]@{
Member1 = 'First'
Member2 = 'Second'
Member3 = 'Third'
}
$Obj3 = [PSCustomObject]@{
Member1 = 'First'
Member2 = 'Second'
Member3 = 'Third'
Member4 = 'Fourth'
}
$Array = $Obj1, $Obj2, $Obj3
$Array | Out-GridView -Title 'Not showing Member3 and Member4'
$Array | Update-FirstObjectProperties | Export-Excel @ExcelParams -WorkSheetname Numbers
Updates the first object of the array by adding property 'Member3' and 'Member4'. Afterwards. all objects are exported to an Excel file and all column headers are visible.
.EXAMPLE
Get-Process | Export-Excel .\test.xlsx -WorkSheetname Processes -IncludePivotTable -Show -PivotRows Company -PivotData PM
@@ -16,28 +188,29 @@ Function Export-Excel {
Get-Process | Export-Excel .\test.xlsx -WorkSheetname Processes -ChartType PieExploded3D -IncludePivotChart -IncludePivotTable -Show -PivotRows Company -PivotData PM
.EXAMPLE
Remove-Item "c:\temp\test.xlsx" -ErrorAction Ignore
Get-Service | Export-Excel "c:\temp\test.xlsx" -Show -IncludePivotTable -PivotRows status -PivotData @{status='count'}
Get-Service | Export-Excel 'c:\temp\test.xlsx' -Show -IncludePivotTable -PivotRows status -PivotData @{status='count'}
.LINK
https://github.com/dfinke/ImportExcel
#>
[CmdLetBinding()]
Param(
#[Parameter(Mandatory=$true)]
$Path,
[Parameter(ValueFromPipeline=$true)]
$TargetData,
[String]$WorkSheetname='Sheet1',
[String]$WorkSheetname = 'Sheet1',
[String]$Title,
[OfficeOpenXml.Style.ExcelFillStyle]$TitleFillPattern='None',
[OfficeOpenXml.Style.ExcelFillStyle]$TitleFillPattern = 'None',
[Switch]$TitleBold,
[Int]$TitleSize=22,
[Int]$TitleSize = 22,
[System.Drawing.Color]$TitleBackgroundColor,
[String[]]$PivotRows,
[String[]]$PivotColumns,
$PivotData,
[Switch]$PivotDataToColumn,
[String]$Password,
[OfficeOpenXml.Drawing.Chart.eChartType]$ChartType='Pie',
[OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie',
[Switch]$IncludePivotTable,
[Switch]$IncludePivotChart,
[Switch]$NoLegend,
@@ -69,43 +242,160 @@ Function Export-Excel {
}
})]
[String]$TableName,
[OfficeOpenXml.Table.TableStyles]$TableStyle='Medium6',
[Object[]]$ConditionalFormat,
[Object[]]$ConditionalText,
[OfficeOpenXml.Table.TableStyles]$TableStyle = 'Medium6',
[Object[]]$ExcelChartDefinition,
[ScriptBlock]$CellStyleSB,
[String[]]$HideSheet,
[Switch]$KillExcel,
[Switch]$AutoNameRange,
$StartRow=1,
$StartColumn=1,
[Int]$StartRow = 1,
[Int]$StartColumn = 1,
[Switch]$PassThru,
[String]$Numberformat='General',
[String]$Numberformat = 'General',
[String[]]$NoNumberConversion,
[Object[]]$ConditionalFormat,
[Object[]]$ConditionalText,
[ScriptBlock]$CellStyleSB,
[Switch]$Now
)
Begin {
$script:Header = $null
if ($KillExcel) {
Function Add-CellValue {
<#
.SYNOPSIS
Save a value in an Excel cell.
.DESCRIPTION
DateTime objects are always converted to a DateTime format in Excel. And formulas are always
saved as formulas.
Numerical values will be converted to numbers as defined in the regional settings of the local
system. In case the parameter 'NoNumberConversion' is used, we don't convert to number and leave
the value 'as is'. In case of conversion failure, we also leave the value 'as is'.
#>
Param (
[Object]$TargetCell,
[Object]$CellValue
)
Switch ($CellValue) {
{($_ -is [String]) -and ($_.StartsWith('='))} {
#region Save an Excel formula
$TargetCell.Formula = $_
Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$_' as formula"
break
#endregion
}
{$_ -is [DateTime]} {
#region Save a date with an international valid format
$TargetCell.Value = $_
$TargetCell.Style.Numberformat.Format = 'm/d/yy h:mm'
Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$_' as date"
break
#endregion
}
{(($NoNumberConversion) -and ($NoNumberConversion -contains $Name)) -or
($NoNumberConversion -eq '*')} {
#regioon Save a value without converting to number
$TargetCell.Value = $_
Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($TargetCell.Value)' unconverted"
break
#endregion
}
Default {
#region Save a value as a number if possible
if ($Number = ConvertTo-Number $_) {
$TargetCell.Value = $Number
$targetCell.Style.Numberformat.Format = $Numberformat
Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($TargetCell.Value)' as number converted from '$_' with format '$Numberformat'"
}
else {
$TargetCell.Value = $_
Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($TargetCell.Value)' as string"
}
break
#endregion
}
}
}
Function Add-Title {
<#
.SYNOPSIS
Add a title row to the Excel worksheet.
#>
$ws.Cells[$Row, $StartColumn].Value = $Title
$ws.Cells[$Row, $StartColumn].Style.Font.Size = $TitleSize
if ($TitleBold) {
#set title to Bold if -TitleBold was specified.
#Otherwise the default will be unbolded.
$ws.Cells[$Row, $StartColumn].Style.Font.Bold = $True
}
$ws.Cells[$Row, $StartColumn].Style.Fill.PatternType = $TitleFillPattern
#can only set TitleBackgroundColor if TitleFillPattern is something other than None
if ($TitleBackgroundColor -AND ($TitleFillPattern -ne 'None')) {
$ws.Cells[$Row, $StartColumn].Style.Fill.BackgroundColor.SetColor($TitleBackgroundColor)
}
else {
Write-Warning "Title Background Color ignored. You must set the TitleFillPattern parameter to a value other than 'None'. Try 'Solid'."
}
}
Function ConvertTo-Number {
<#
.SYNOPSIS
Convert a value to a number
#>
Param (
[String]$Value
)
$R = $null
if ([Double]::TryParse([String]$Value,[System.Globalization.NumberStyles]::Any,
[System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$R)) {
$R
}
}
Function Stop-ExcelProcess {
<#
.SYNOPSIS
Stop the Excel process when it's running.
#>
Get-Process excel -ErrorAction Ignore | Stop-Process
while (Get-Process excel -ErrorAction Ignore) {}
}
Try {
$script:Header = $null
if ($KillExcel) {
Stop-ExcelProcess
}
if ($Now) {
$Path=[System.IO.Path]::GetTempFileName() -replace '\.tmp','.xlsx'
$Show=$true
$AutoSize=$true
$AutoFilter=$true
$Path = [System.IO.Path]::GetTempFileName() -replace '\.tmp','.xlsx'
$Show = $true
$AutoSize = $true
$AutoFilter = $true
}
$Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
if (Test-Path $path) {
Write-Debug "File `"$Path`" already exists"
if (Test-Path $Path) {
Write-Debug "Path '$Path' already exists"
}
$pkg = New-Object OfficeOpenXml.ExcelPackage $Path
$pkg = New-Object OfficeOpenXml.ExcelPackage $Path
$ws = $pkg | Add-WorkSheet -WorkSheetname $WorkSheetname -NoClobber:$NoClobber
foreach ($format in $ConditionalFormat ) {
@@ -114,147 +404,86 @@ Function Export-Excel {
$rule.Reverse = $format.Reverse
}
# Force at least one cell value
#$ws.Cells[1, 1].Value = ''
$Row = $StartRow
if ($Title) {
$ws.Cells[$Row, $StartColumn].Value = $Title
$ws.Cells[$Row, $StartColumn].Style.Font.Size = $TitleSize
if ($TitleBold) {
#set title to Bold if -TitleBold was specified.
#Otherwise the default will be unbolded.
$ws.Cells[$Row, $StartColumn].Style.Font.Bold = $True
}
$ws.Cells[$Row, $StartColumn].Style.Fill.PatternType = $TitleFillPattern
#can only set TitleBackgroundColor if TitleFillPattern is something other than None
if ($TitleBackgroundColor -AND ($TitleFillPattern -ne 'None')) {
$ws.Cells[$Row, $StartColumn].Style.Fill.BackgroundColor.SetColor($TitleBackgroundColor)
}
else {
Write-Warning "Title Background Color ignored. You must set the TitleFillPattern parameter to a value other than 'None'. Try 'Solid'."
}
Add-Title
$Row += 1
}
$firstTimeThru = $true
$isDataTypeValueType = $false
$pattern = 'string|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort'
}
Catch {
if ($AlreadyExists) {
throw "$WorkSheetname already exists."
} else {
throw $Error[0].Exception.Message
throw "Failed exporting worksheet '$WorkSheetname' to '$Path': The worksheet '$WorkSheetname' already exists."
}
else {
throw "Failed exporting worksheet '$WorkSheetname' to '$Path': $_"
}
}
$firstTimeThru = $true
$isDataTypeValueType=$false
$pattern = "string|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort"
}
Process {
if ($firstTimeThru) {
$firstTimeThru=$false
$isDataTypeValueType = $TargetData.GetType().name -match "string|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort"
Write-Verbose "DataTypeName is '$($TargetData.GetType().name)' isDataTypeValueType $isDataTypeValueType"
}
if ($isDataTypeValueType) {
$ColumnIndex = $StartColumn
$targetCell = $ws.Cells[$Row, $ColumnIndex]
$r=$null
$cellValue=$TargetData
if ([Double]::TryParse([string]$cellValue,[System.Globalization.NumberStyles]::Any,[System.Globalization.NumberFormatInfo]::CurrentInfo, [ref]$r)) {
$targetCell.Value = $r
$targetCell.Style.Numberformat.Format=$Numberformat
} else {
$targetCell.Value = $cellValue
Try {
if ($firstTimeThru) {
$firstTimeThru = $false
$isDataTypeValueType = $TargetData.GetType().name -match $pattern
Write-Debug "DataTypeName is '$($TargetData.GetType().name)' isDataTypeValueType '$isDataTypeValueType'"
}
switch ($TargetData.$Name) {
{$_ -is [datetime]} {$targetCell.Style.Numberformat.Format = "m/d/yy h:mm"}
}
$ColumnIndex += 1
$Row += 1
} else {
if (!$script:Header) {
if ($isDataTypeValueType) {
$ColumnIndex = $StartColumn
$script:Header = $TargetData.psobject.properties.name
if ($NoHeader) {
# Don't push the headers to the spread sheet
$Row -= 1
} else {
foreach ($Name in $script:Header) {
Write-Verbose "Add header '$Name'"
$ws.Cells[$Row, $ColumnIndex].Value = $Name
$ColumnIndex += 1
}
}
}
$Row += 1
$ColumnIndex = $StartColumn
foreach ($Name in $script:Header) {
$targetCell = $ws.Cells[$Row, $ColumnIndex]
$cellValue=$TargetData.$Name
if ($cellValue -is [string] -and $cellValue.StartsWith('=')) {
$targetCell.Formula = $cellValue
} else {
$r=$null
if ([Double]::TryParse([string]$cellValue,[System.Globalization.NumberStyles]::Any,[System.Globalization.NumberFormatInfo]::CurrentInfo, [ref]$r)) {
$targetCell.Value = $r
$targetCell.Style.Numberformat.Format=$Numberformat
Write-Verbose "Add cell value '$r' in Numberformat '$Numberformat'"
} else {
$targetCell.Value = $cellValue
Write-Verbose "Add cell value '$cellValue' as String"
}
}
switch ($TargetData.$Name) {
{$_ -is [datetime]} {
$targetCell.Style.Numberformat.Format = "m/d/yy h:mm"
}
}
#[ref]$uriResult=$null
#if ([uri]::TryCreate($cellValue, [System.UriKind]::Absolute, $uriResult)) {
# $targetCell.Hyperlink = [uri]$cellValue
# $namedStyle=$ws.Workbook.Styles.NamedStyles | where {$_.Name -eq 'HyperLink'}
# if (!$namedStyle) {
# $namedStyle=$ws.Workbook.Styles.CreateNamedStyle("HyperLink")
# $namedStyle.Style.Font.UnderLine = $true
# $namedStyle.Style.Font.Color.SetColor("Blue")
# }
# $targetCell.StyleName = "HyperLink"
#}
Add-CellValue -TargetCell $ws.Cells[$Row, $ColumnIndex] -CellValue $TargetData
$ColumnIndex += 1
$Row += 1
}
else {
#region Add headers
if (-not $script:Header) {
$ColumnIndex = $StartColumn
$script:Header = $TargetData.PSObject.Properties.Name
if ($NoHeader) {
# Don't push the headers to the spread sheet
$Row -= 1
}
else {
foreach ($Name in $script:Header) {
$ws.Cells[$Row, $ColumnIndex].Value = $Name
Write-Verbose "Cell '$Row`:$ColumnIndex' add header '$Name'"
$ColumnIndex += 1
}
}
}
#endregion
$Row += 1
$ColumnIndex = $StartColumn
foreach ($Name in $script:Header) {
#region Add non header values
Add-CellValue -TargetCell $ws.Cells[$Row, $ColumnIndex] -CellValue $TargetData.$Name
$ColumnIndex += 1
#endregion
}
}
}
Catch {
throw "Failed exporting worksheet '$WorkSheetname' to '$Path': $_"
}
}
End {
Try {
if ($AutoNameRange) {
$totalRows=$ws.Dimension.Rows
$totalColumns=$ws.Dimension.Columns
$totalRows = $ws.Dimension.Rows
$totalColumns = $ws.Dimension.Columns
foreach($c in 0..($totalColumns-1)) {
$targetRangeName = "$($script:Header[$c])"
@@ -269,63 +498,67 @@ Function Export-Excel {
}
}
$startAddress=$ws.Dimension.Start.Address
$dataRange="{0}:{1}" -f $startAddress, $ws.Dimension.End.Address
if ($Title) {
$startAddress = "A2"
}
else {
$startAddress = $ws.Dimension.Start.Address
}
$dataRange = "{0}:{1}" -f $startAddress, $ws.Dimension.End.Address
Write-Debug "Data Range $dataRange"
Write-Debug "Data Range '$dataRange'"
if (-not [string]::IsNullOrEmpty($RangeName)) {
if (-not [String]::IsNullOrEmpty($RangeName)) {
$ws.Names.Add($RangeName, $ws.Cells[$dataRange]) | Out-Null
}
if (-not [string]::IsNullOrEmpty($TableName)) {
#$ws.Tables.Add($ws.Cells[$dataRange], $TableName) | Out-Null
#"$($StartRow),$($StartColumn),$($ws.Dimension.End.Row-$StartRow),$($Header.Count)"
$csr=$StartRow
$csc=$StartColumn
$cer=$ws.Dimension.End.Row #-$StartRow+1
$cec=$script:Header.Count
$targetRange=$ws.Cells[$csr, $csc, $cer,$cec]
if (-not [String]::IsNullOrEmpty($TableName)) {
$csr = $StartRow
if ($Title) {
$csr += 1
}
$csc = $StartColumn
$cer = $ws.Dimension.End.Row
$cec = $script:Header.Count
$targetRange = $ws.Cells[$csr, $csc, $cer,$cec]
$tbl = $ws.Tables.Add($targetRange, $TableName)
$tbl.TableStyle=$TableStyle
$tbl.TableStyle = $TableStyle
}
if ($IncludePivotTable) {
$pivotTableName = $WorkSheetname + "PivotTable"
$pivotTableName = $WorkSheetname + 'PivotTable'
$wsPivot = $pkg | Add-WorkSheet -WorkSheetname $pivotTableName -NoClobber:$NoClobber
$wsPivot.View.TabSelected = $true
$pivotTableDataName=$WorkSheetname + "PivotTableData"
$pivotTableDataName=$WorkSheetname + 'PivotTableData'
if ($Title) {$startAddress="A2"}
$pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells["A1"], $ws.Cells[$dataRange], $pivotTableDataName)
$pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells['A1'], $ws.Cells[$dataRange], $pivotTableDataName)
if ($PivotRows) {
foreach ($Row in $PivotRows) {
$null=$pivotTable.RowFields.Add($pivotTable.Fields[$Row])
$null = $pivotTable.RowFields.Add($pivotTable.Fields[$Row])
}
}
if ($PivotColumns) {
foreach ($Column in $PivotColumns) {
$null=$pivotTable.ColumnFields.Add($pivotTable.Fields[$Column])
$null = $pivotTable.ColumnFields.Add($pivotTable.Fields[$Column])
}
}
if ($PivotData) {
if ($PivotData -is [hashtable] -or $PivotData -is [System.Collections.Specialized.OrderedDictionary]) {
$PivotData.Keys | % {
$df=$pivotTable.DataFields.Add($pivotTable.Fields[$_])
if ($PivotData -is [HashTable] -or $PivotData -is [System.Collections.Specialized.OrderedDictionary]) {
$PivotData.Keys | ForEach-Object {
$df = $pivotTable.DataFields.Add($pivotTable.Fields[$_])
$df.Function = $PivotData.$_
}
} else {
}
else {
foreach ($Item in $PivotData) {
$df=$pivotTable.DataFields.Add($pivotTable.Fields[$Item])
$df = $pivotTable.DataFields.Add($pivotTable.Fields[$Item])
$df.Function = 'Count'
}
}
@@ -335,60 +568,76 @@ Function Export-Excel {
}
if ($IncludePivotChart) {
$chart = $wsPivot.Drawings.AddChart("PivotChart", $ChartType, $pivotTable)
$chart = $wsPivot.Drawings.AddChart('PivotChart', $ChartType, $pivotTable)
$chart.DataLabel.ShowCategory = $ShowCategory
$chart.DataLabel.ShowPercent = $ShowPercent
$chart.DataLabel.ShowCategory=$ShowCategory
$chart.DataLabel.ShowPercent=$ShowPercent
if ($NoLegend) { $chart.Legend.Remove() }
if ($NoLegend) {
$chart.Legend.Remove()
}
$chart.SetPosition(1, 0, 6, 0)
$chart.SetSize(600, 400)
}
}
if ($Password) { $ws.Protection.SetPassword($Password) }
if ($Password) {
$ws.Protection.SetPassword($Password)
}
if ($AutoFilter) {
$ws.Cells[$dataRange].AutoFilter=$true
}
if ($FreezeTopRow) { $ws.View.FreezePanes(2,1) }
if ($FreezeTopRowFirstColumn) { $ws.View.FreezePanes(2,2) }
if ($FreezeFirstColumn) { $ws.View.FreezePanes(1,2) }
if ($FreezeTopRow) {
$ws.View.FreezePanes(2,1)
}
if ($FreezeTopRowFirstColumn) {
$ws.View.FreezePanes(2,2)
}
if ($FreezeFirstColumn) {
$ws.View.FreezePanes(1,2)
}
if ($FreezePane) {
$freezeRow,$freezeColumn=$FreezePane
if (!$freezeColumn -or $freezeColumn -eq 0) {$freezeColumn=1}
if (-not $freezeColumn -or $freezeColumn -eq 0) {
$freezeColumn=1
}
if ($freezeRow -gt 1) {
$ws.View.FreezePanes($freezeRow,$freezeColumn)
}
}
if ($BoldTopRow) {
$range=$ws.Dimension.Address -replace $ws.Dimension.Rows, "1"
$ws.Cells[$range].Style.Font.Bold=$true
if ($Title) {
$range = $ws.Dimension.Address -replace '\d+', '2'
}
else {
$range = $ws.Dimension.Address -replace '\d+', '1'
}
$ws.Cells[$range].Style.Font.Bold = $true
}
if ($AutoSize) {
$ws.Cells.AutoFitColumns()
}
if ($AutoSize) { $ws.Cells.AutoFitColumns() }
#$pkg.Workbook.View.ActiveTab = $ws.SheetID
foreach ($Sheet in $HideSheet) {
$pkg.Workbook.WorkSheets[$Sheet].Hidden="Hidden"
$pkg.Workbook.WorkSheets[$Sheet].Hidden = 'Hidden'
}
$chartCount=0
foreach ($chartDef in $ExcelChartDefinition) {
$ChartName = "Chart"+(Split-Path -Leaf ([System.IO.path]::GetTempFileName())) -replace 'tmp|\.',''
$ChartName = 'Chart' + (Split-Path -Leaf ([System.IO.path]::GetTempFileName())) -replace 'tmp|\.',''
$chart = $ws.Drawings.AddChart($ChartName, $chartDef.ChartType)
$chart.Title.Text = $chartDef.Title
if ($chartDef.NoLegend) {
$chart.Legend.Remove()
}
#$chart.Datalabel.ShowLegendKey = $true
if ($chart.Datalabel -ne $null) {
$chart.Datalabel.ShowCategory = $chartDef.ShowCategory
@@ -403,21 +652,25 @@ Function Export-Excel {
$Series=$chart.Series.Add($chartDef.YRange, $chartDef.XRange)
$SeriesHeader=$chartDef.SeriesHeader
if (!$SeriesHeader) {$SeriesHeader="Series 1"}
if (-not $SeriesHeader) {
$SeriesHeader = 'Series 1'
}
$Series.Header = $SeriesHeader
} else {
for($idx=0; $idx -lt $chartDefCount; $idx+=1) {
for($idx = 0; $idx -lt $chartDefCount; $idx += 1) {
$Series=$chart.Series.Add($chartDef.YRange[$idx], $chartDef.XRange)
if ($chartDef.SeriesHeader.Count -gt 0) {
$SeriesHeader=$chartDef.SeriesHeader[$idx]
$SeriesHeader = $chartDef.SeriesHeader[$idx]
}
if (!$SeriesHeader) {$SeriesHeader="Series $($idx)"}
if (-not $SeriesHeader) {
$SeriesHeader = "Series $($idx)"
}
$Series.Header = $SeriesHeader
$SeriesHeader=$null
$SeriesHeader = $null
}
}
}
@@ -427,41 +680,47 @@ Function Export-Excel {
$target = "Add$($targetConditionalText.ConditionalType)"
$Range=$targetConditionalText.Range
if (!$Range) { $Range=$ws.Dimension.Address }
if (-not $Range) {
$Range = $ws.Dimension.Address
}
$rule=($ws.Cells[$Range].ConditionalFormatting).PSObject.Methods[$target].Invoke()
if ($targetConditionalText.Text) {
if ($targetConditionalText.ConditionalType -match "equal|notequal|lessthan|lessthanorequal|greaterthan|greaterthanorequal") {
$rule.Formula= $targetConditionalText.Text
} else {
if ($targetConditionalText.ConditionalType -match 'equal|notequal|lessthan|lessthanorequal|greaterthan|greaterthanorequal') {
$rule.Formula = $targetConditionalText.Text
}
else {
$rule.Text = $targetConditionalText.Text
}
}
$rule.Style.Font.Color.Color = $targetConditionalText.ConditionalTextColor
$rule.Style.Fill.PatternType=$targetConditionalText.PatternType
$rule.Style.Fill.BackgroundColor.Color=$targetConditionalText.BackgroundColor
}
$rule.Style.Fill.PatternType = $targetConditionalText.PatternType
$rule.Style.Fill.BackgroundColor.Color = $targetConditionalText.BackgroundColor
}
}
if ($CellStyleSB) {
$TotalRows=$ws.Dimension.Rows
$LastColumn=(Get-ExcelColumnName $ws.Dimension.Columns).ColumnName
$TotalRows = $ws.Dimension.Rows
$LastColumn = (Get-ExcelColumnName $ws.Dimension.Columns).ColumnName
& $CellStyleSB $ws $TotalRows $LastColumn
}
if ($PassThru) {
$pkg
} else {
}
else {
$pkg.Save()
$pkg.Dispose()
if ($Show) {Invoke-Item $Path}
if ($Show) {
Invoke-Item $Path
}
}
}
Catch {
throw "Failed exporting the worksheet: $_"
throw "Failed exporting worksheet '$WorkSheetname' to '$Path': $_"
}
}
}

2073
ImportExcel.Tests.ps1 Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
RootModule = 'ImportExcel.psm1'
# Version number of this module.
ModuleVersion = '2.4.0'
ModuleVersion = '4.0.2'
# ID used to uniquely identify this module
GUID = '60dd4136-feff-401a-ba27-a84458c57ede'

View File

@@ -1,181 +1,429 @@
Add-Type -Path "$($PSScriptRoot)\EPPlus.dll"
. $PSScriptRoot\Export-Excel.ps1
. $PSScriptRoot\New-ConditionalFormattingIconSet.ps1
. $PSScriptRoot\New-ConditionalText.ps1
. $PSScriptRoot\Export-ExcelSheet.ps1
. $PSScriptRoot\New-ExcelChart.ps1
. $PSScriptRoot\Invoke-Sum.ps1
. $PSScriptRoot\InferData.ps1
. $PSScriptRoot\Get-ExcelColumnName.ps1
. $PSScriptRoot\Get-XYRange.ps1
. $PSScriptRoot\Charting.ps1
. $PSScriptRoot\New-PSItem.ps1
. $PSScriptRoot\Pivot.ps1
. $PSScriptRoot\ConvertFromExcelData.ps1
. $PSScriptRoot\ConvertFromExcelToSQLInsert.ps1
. $PSScriptRoot\ConvertToExcelXlsx.ps1
. $PSScriptRoot\Copy-ExcelWorkSheet.ps1
. $PSScriptRoot\Export-Excel.ps1
. $PSScriptRoot\Export-ExcelSheet.ps1
. $PSScriptRoot\Get-ExcelColumnName.ps1
. $PSScriptRoot\Get-ExcelSheetInfo.ps1
. $PSScriptRoot\Get-ExcelWorkbookInfo.ps1
. $PSScriptRoot\Get-HtmlTable.ps1
. $PSScriptRoot\Import-Html.ps1
. $PSScriptRoot\Get-Range.ps1
. $PSScriptRoot\TrackingUtils.ps1
. $PSScriptRoot\Copy-ExcelWorkSheet.ps1
. $PSScriptRoot\Get-XYRange.ps1
. $PSScriptRoot\Import-Html.ps1
. $PSScriptRoot\InferData.ps1
. $PSScriptRoot\Invoke-Sum.ps1
. $PSScriptRoot\New-ConditionalFormattingIconSet.ps1
. $PSScriptRoot\New-ConditionalText.ps1
. $PSScriptRoot\New-ExcelChart.ps1
. $PSScriptRoot\New-PSItem.ps1
. $PSScriptRoot\Pivot.ps1
. $PSScriptRoot\Set-CellStyle.ps1
. $PSScriptRoot\TrackingUtils.ps1
. $PSScriptRoot\Update-FirstObjectProperties.ps1
if($PSVersionTable.PSVersion.Major -ge 5) {
. $PSScriptRoot\plot.ps1
function New-Plot {
if ($PSVersionTable.PSVersion.Major -ge 5) {
. $PSScriptRoot\Plot.ps1
Function New-Plot {
[OutputType([PSPlot])]
param()
Param()
[psplot]::new()
[PSPlot]::new()
}
} else {
Write-Warning "PowerShell 5 is required for plot.ps1"
Write-Warning "PowerShell Excel is ready, except for that functionality"
}
else {
Write-Warning 'PowerShell 5 is required for plot.ps1'
Write-Warning 'PowerShell Excel is ready, except for that functionality'
}
function Import-Excel {
<#
Function Import-Excel {
<#
.SYNOPSIS
Read the content of an Excel sheet.
Create custom objects from the rows in an Excel worksheet.
.DESCRIPTION
The Import-Excel cmdlet reads the content of an Excel worksheet and creates one object for each row. This is done without using Microsoft Excel in the background but by using the .NET EPPLus.dll. You can also automate the creation of Pivot Tables and Charts.
The Import-Excel cmdlet creates custom objects from the rows in an Excel worksheet. Each row represents one object. All of this is possible without installing Microsoft Excel and by using the .NET library <20>EPPLus.dll<EFBFBD>.
By default, the property names of the objects are retrieved from the column headers. Because an object cannot have a blanc property name, only columns with column headers will be imported.
If the default behavior is not desired and you want to import the complete worksheet <20>as is<69>, the parameter <20>-NoHeader<65> can be used. In case you want to provide your own property names, you can use the parameter <20>-HeaderName<6D>.
.PARAMETER Path
Specifies the path to the Excel file.
.PARAMETER WorkSheetname
Specifies the name of the worksheet in the Excel workbook.
.PARAMETER HeaderRow
Specifies custom header names for columns.
.PARAMETER Header
Specifies the title used in the worksheet. The title is placed on the first line of the worksheet.
.PARAMETER NoHeader
When used we generate our own headers (P1, P2, P3, ..) instead of the ones defined in the first row of the Excel worksheet.
.PARAMETER 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.
.PARAMETER DataOnly
When used we will only generate objects for rows that contain text values, not for empty rows or columns.
.EXAMPLE
Import-Excel -WorkSheetname 'Statistics' -Path 'E:\Finance\Company results.xlsx'
Imports all the information found in the worksheet 'Statistics' of the Excel file 'Company results.xlsx'
Import only rows and columns that contain data, empty rows and empty columns are not imported.
.PARAMETER HeaderName
Specifies custom property names to use, instead of the values defined in the column headers of the TopRow.
In case you provide less header names than there is data in the worksheet, then only the data with a corresponding header name will be imported and the data without header name will be disregarded.
In case you provide more header names than there is data in the worksheet, then all data will be imported and all objects will have all the property names you defined in the header names. As such, the last properties will be blanc as there is no data for them.
.PARAMETER NoHeader
Automatically generate property names (P1, P2, P3, ..) instead of the ones defined in the column headers of the TopRow.
This switch is best used when you want to import the complete worksheet <20>as is<69> and are not concerned with the property names.
.PARAMETER StartRow
The row from where we start to import data, all rows above the StartRow are disregarded. By default this is the first row.
When the parameters <20>-NoHeader<65> and <20>-HeaderName<6D> are not provided, this row will contain the column headers that will be used as property names. When one of both parameters are provided, the property names are automatically created and this row will be treated as a regular row containing data.
.PARAMETER Password
Accepts a string that will be used to open a password protected Excel file.
.EXAMPLE
Import data from an Excel worksheet. One object is created for each row. The property names of the objects consist of the column names defined in the first row. In case a column doesn<73>t have a column header (usually in row 1 when <20>-StartRow<6F> is not used), then the unnamed columns will be skipped and the data in those columns will not be imported.
----------------------------------------------
| File: Movies.xlsx - Sheet: Actors |
----------------------------------------------
| A B C |
|1 First Name Address |
|2 Chuck Norris California |
|3 Jean-Claude Vandamme Brussels |
----------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors
First Name: Chuck
Address : California
First Name: Jean-Claude
Address : Brussels
Notice that column 'B' is not imported because there's no value in cell 'B1' that can be used as property name for the objects.
.EXAMPLE
Import the complete Excel worksheet <20>as is<69> by using the <20>-NoHeader<65> switch. One object is created for each row. The property names of the objects will be automatically generated (P1, P2, P3, ..).
----------------------------------------------
| File: Movies.xlsx - Sheet: Actors |
----------------------------------------------
| A B C |
|1 First Name Address |
|2 Chuck Norris California |
|3 Jean-Claude Vandamme Brussels |
----------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors -NoHeader
P1: First Name
P2:
P3: Address
P1: Chuck
P2: Norris
P3: California
P1: Jean-Claude
P2: Vandamme
P3: Brussels
Notice that the column header (row 1) is imported as an object too.
.EXAMPLE
Import data from an Excel worksheet. One object is created for each row. The property names of the objects consist of the names defined in the parameter <20>-HeaderName<6D>. The properties are named starting from the most left column (A) to the right. In case no value is present in one of the columns, that property will have an empty value.
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Movies |
----------------------------------------------------------
| A B C D |
|1 The Bodyguard 1992 9 |
|2 The Matrix 1999 8 |
|3 |
|4 Skyfall 2012 9 |
----------------------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Movies -HeaderName 'Movie name', 'Year', 'Rating', 'Genre'
Movie name: The Bodyguard
Year : 1992
Rating : 9
Genre :
Movie name: The Matrix
Year : 1999
Rating : 8
Genre :
Movie name:
Year :
Rating :
Genre :
Movie name: Skyfall
Year : 2012
Rating : 9
Genre :
Notice that empty rows are imported and that data for the property 'Genre' is not present in the worksheet. As such, the 'Genre' property will be blanc for all objects.
.EXAMPLE
Import data from an Excel worksheet. One object is created for each row. The property names of the objects are automatically generated by using the switch <20>-NoHeader<65> (P1, P@, P#, ..). The switch <20>-DataOnly<6C> will speed up the import because empty rows and empty columns are not imported.
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Movies |
----------------------------------------------------------
| A B C D |
|1 The Bodyguard 1992 9 |
|2 The Matrix 1999 8 |
|3 |
|4 Skyfall 2012 9 |
----------------------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Movies <20>NoHeader -DataOnly
P1: The Bodyguard
P2: 1992
P3: 9
P1: The Matrix
P2: 1999
P3: 8
P1: Skyfall
P2: 2012
P3: 9
Notice that empty rows and empty columns are not imported.
.EXAMPLE
Import data from an Excel worksheet. One object is created for each row. The property names are provided with the <20>-HeaderName<6D> parameter. The import will start from row 2 and empty columns and rows are not imported.
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Actors |
----------------------------------------------------------
| A B C D |
|1 Chuck Norris California |
|2 |
|3 Jean-Claude Vandamme Brussels |
----------------------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors -DataOnly -HeaderName 'FirstName', 'SecondName', 'City' <20>StartRow 2
FirstName : Jean-Claude
SecondName: Vandamme
City : Brussels
Notice that only 1 object is imported with only 3 properties. Column B and row 2 are empty and have been disregarded by using the switch '-DataOnly'. The property names have been named with the values provided with the parameter '-HeaderName'. Row number 1 with <20>Chuck Norris<69> has not been imported, because we started the import from row 2 with the parameter <20>-StartRow 2<>.
.LINK
https://github.com/dfinke/ImportExcel
.NOTES
#>
param(
[Alias("FullName")]
[Parameter(ValueFromPipelineByPropertyName=$true, ValueFromPipeline=$true, Mandatory=$true)]
[ValidateScript({ Test-Path $_ -PathType Leaf })]
$Path,
[Alias("Sheet")]
$WorkSheetname=1,
[int]$HeaderRow=1,
[string[]]$Header,
[switch]$NoHeader,
[switch]$DataOnly
[CmdLetBinding(DefaultParameterSetName)]
Param (
[Alias('FullName')]
[Parameter(ValueFromPipelineByPropertyName, ValueFromPipeline, Position=0, Mandatory)]
[ValidateScript({(Test-Path -Path $_ -PathType Leaf) -and ($_ -match '.xls$|.xlsx$')})]
[String]$Path,
[Alias('Sheet')]
[Parameter(Position=1)]
[ValidateNotNullOrEmpty()]
[String]$WorksheetName,
[Parameter(ParameterSetName='B', Mandatory)]
[String[]]$HeaderName,
[Parameter(ParameterSetName='C', Mandatory)]
[Switch]$NoHeader,
[Alias('HeaderRow','TopRow')]
[ValidateRange(1, 9999)]
[Int]$StartRow,
[Switch]$DataOnly,
[ValidateNotNullOrEmpty()]
[String]$Password
)
Process {
Begin {
Function Add-Property {
<#
.SYNOPSIS
Add the property name and value to the hashtable that will create a new object for each row.
#>
$Path = (Resolve-Path $Path).ProviderPath
write-debug "target excel file $Path"
$stream = New-Object -TypeName System.IO.FileStream -ArgumentList $Path,"Open","Read","ReadWrite"
$xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $stream
$workbook = $xl.Workbook
$worksheet=$workbook.Worksheets[$WorkSheetname]
$dimension=$worksheet.Dimension
$Rows=$dimension.Rows
$Columns=$dimension.Columns
if ($NoHeader) {
if ($DataOnly) {
$CellsWithValues = $worksheet.Cells | where Value
$Script:i = 0
$ColumnReference = $CellsWithValues | Select-Object -ExpandProperty End | Group-Object Column |
Select-Object @{L='Column';E={$_.Name}}, @{L='NewColumn';E={$Script:i++; $Script:i}}
$CellsWithValues | Select-Object -ExpandProperty End | Group-Object Row | ForEach-Object {
$newRow = [Ordered]@{}
foreach ($C in $ColumnReference) {
$newRow."P$($C.NewColumn)" = $worksheet.Cells[($_.Name),($C.Column)].Value
}
[PSCustomObject]$newRow
}
Param (
[Parameter(Mandatory)]
[String]$Name,
$Value
)
Try {
$NewRow.$Name = $Value
Write-Verbose "Import cell '$($Worksheet.Cells[$R, $P.Column].Address)' with property name '$Name' and value '$Value'"
}
else {
foreach ($Row in 0..($Rows-1)) {
$newRow = [Ordered]@{}
foreach ($Column in 0..($Columns-1)) {
$propertyName = "P$($Column+1)"
$newRow.$propertyName = $worksheet.Cells[($Row+1),($Column+1)].Value
}
[PSCustomObject]$newRow
}
}
}
else {
if (!$Header) {
$Header = foreach ($Column in 1..$Columns) {
$worksheet.Cells[$HeaderRow,$Column].Value
}
}
if ($Rows -eq 1) {
$Header | ForEach {$h=[Ordered]@{}} {$h.$_=''} {[PSCustomObject]$h}
}
else {
if ($DataOnly) {
$CellsWithValues = $worksheet.Cells | where {$_.Value -and ($_.End.Row -ne 1)}
$Script:i = -1
$ColumnReference = $CellsWithValues | Select-Object -ExpandProperty End | Group-Object Column |
Select-Object @{L='Column';E={$_.Name}}, @{L='NewColumn';E={$Script:i++; $Header[$Script:i]}}
$CellsWithValues | Select-Object -ExpandProperty End | Group-Object Row | ForEach-Object {
$newRow = [Ordered]@{}
foreach ($C in $ColumnReference) {
$newRow."$($C.NewColumn)" = $worksheet.Cells[($_.Name),($C.Column)].Value
}
[PSCustomObject]$newRow
}
}
else {
foreach ($Row in ($HeaderRow+1)..$Rows) {
$h=[Ordered]@{}
foreach ($Column in 0..($Columns-1)) {
if($Header[$Column].Length -gt 0) {
$Name = $Header[$Column]
$h.$Name = $worksheet.Cells[$Row,($Column+1)].Value
}
}
[PSCustomObject]$h
}
}
Catch {
throw "Failed adding the property name '$Name' with value '$Value': $_"
}
}
$stream.Close()
$stream.Dispose()
$xl.Dispose()
$xl = $null
Function Get-PropertyNames {
<#
.SYNOPSIS
Create objects containing the column number and the column name for each of the different header types.
#>
Param (
[Parameter(Mandatory)]
[Int[]]$Columns,
[Parameter(Mandatory)]
[Int]$StartRow
)
Try {
if ($NoHeader) {
$i = 0
foreach ($C in $Columns) {
$i++
$C | Select-Object @{N='Column'; E={$_}}, @{N='Value'; E={'P' + $i}}
}
}
elseif ($HeaderName) {
$i = 0
foreach ($H in $HeaderName) {
$H | Select-Object @{N='Column'; E={$Columns[$i]}}, @{N='Value'; E={$H}}
$i++
}
}
else {
if ($StartRow -eq 0) {
throw 'The top row can never be equal to 0 when we need to retrieve headers from the worksheet.'
}
foreach ($C in $Columns) {
$Worksheet.Cells[$StartRow,$C] | where {$_.Value} | Select-Object @{N='Column'; E={$C}}, Value
}
}
}
Catch {
throw "Failed creating property names: $_"
}
}
}
Process {
Try {
#region Open file
$Path = (Resolve-Path $Path).ProviderPath
Write-Verbose "Import Excel workbook '$Path' with worksheet '$Worksheetname'"
$Stream = New-Object -TypeName System.IO.FileStream -ArgumentList $Path, 'Open', 'Read', 'ReadWrite'
if ($Password) {
$Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage
Try {
$Excel.Load($Stream,$Password)
}
Catch {
throw "Password '$Password' is not correct."
}
}
else {
$Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Stream
}
#endregion
#region Select worksheet
if ($WorksheetName) {
if (-not ($Worksheet = $Excel.Workbook.Worksheets[$WorkSheetName])) {
throw "Worksheet '$WorksheetName' not found, the workbook only contains the worksheets '$($Excel.Workbook.Worksheets)'. If you only wish to select the first worksheet, please remove the '-WorksheetName' parameter."
}
}
else {
$Worksheet = $Excel.Workbook.Worksheets | Select-Object -First 1
}
#endregion
#region Set the top row
if (((-not ($NoHeader -or $HeaderName)) -and ($StartRow -eq 0))) {
$StartRow = 1
}
#endregion
if (-not ($AllCells = $Worksheet.Cells | where {($_.Start.Row -ge $StartRow)})) {
Write-Warning "Worksheet '$WorksheetName' in workbook '$Path' is empty after StartRow '$StartRow'"
}
else {
#region Get rows and columns
if ($DataOnly) {
$CellsWithValues = $AllCells | where {$_.Value}
$Columns = $CellsWithValues.Start.Column | Sort-Object -Unique
$Rows = $CellsWithValues.Start.Row | Sort-Object -Unique
}
else {
$LastColumn = $AllCells.Start.Column | Sort-Object -Unique | Select-Object -Last 1
$Columns = 1..$LastColumn
$LastRow = $AllCells.Start.Row | Sort-Object -Unique | Select-Object -Last 1
$Rows = $StartRow..$LastRow | where {($_ -ge $StartRow) -and ($_ -gt 0)}
}
#endregion
#region Create property names
if ((-not $Columns) -or (-not ($PropertyNames = Get-PropertyNames -Columns $Columns -StartRow $StartRow))) {
throw "No column headers found on top row '$StartRow'. If column headers in the worksheet are not a requirement then please use the '-NoHeader' or '-HeaderName' parameter."
}
if ($Duplicates = $PropertyNames | Group-Object Value | where Count -GE 2) {
throw "Duplicate column headers found on row '$StartRow' in columns '$($Duplicates.Group.Column)'. Column headers must be unique, if this is not a requirement please use the '-NoHeader' or '-HeaderName' parameter."
}
#endregion
#region Filter out rows with data in columns that don't have a column header
if ($DataOnly -and (-not $NoHeader)) {
$Rows = $CellsWithValues.Start | where {$PropertyNames.Column -contains $_.Column} |
Sort-Object Row -Unique | Select-Object -ExpandProperty Row
}
#endregion
#region Filter out the top row when it contains column headers
if (-not ($NoHeader -or $HeaderName)) {
$Rows = $Rows | where {$_ -gt $StartRow}
}
#endregion
if (-not $Rows) {
Write-Warning "Worksheet '$WorksheetName' in workbook '$Path' contains no data in the rows after top row '$StartRow'"
}
else {
#region Create one object per row
foreach ($R in $Rows) {
Write-Verbose "Import row '$R'"
$NewRow = [Ordered]@{}
foreach ($P in $PropertyNames) {
Add-Property -Name $P.Value -Value $Worksheet.Cells[$R, $P.Column].Value
}
[PSCustomObject]$NewRow
}
#endregion
}
}
}
Catch {
throw "Failed importing the Excel workbook '$Path' with worksheet '$Worksheetname': $_"
}
Finally {
$Stream.Close()
$Stream.Dispose()
$Excel.Dispose()
$Excel = $null
}
}
}

View File

@@ -1,52 +1,94 @@
param([string]$InstallDirectory)
<#
.SYNOPSIS
Download the module files from GitHub.
$fileList = echo `
EPPlus.dll `
ImportExcel.psd1 `
ImportExcel.psm1 `
Export-Excel.ps1 `
New-ConditionalFormattingIconSet.ps1 `
Export-ExcelSheet.ps1 `
New-ExcelChart.ps1 `
Invoke-Sum.ps1 `
InferData.ps1 `
Get-ExcelColumnName.ps1 `
Get-XYRange.ps1 `
Charting.ps1 `
New-PSItem.ps1 `
Pivot.ps1 `
New-ConditionalText.ps1 `
Get-HtmlTable.ps1 `
Import-Html.ps1 `
Get-ExcelSheetInfo.ps1 `
Get-ExcelWorkbookInfo.ps1 `
Get-Range.ps1 `
TrackingUtils.ps1 `
Copy-ExcelWorkSheet.ps1 `
Set-CellStyle.ps1 `
plot.ps1
.DESCRIPTION
Download the module files from GitHub to the local client in the module folder.
#>
if ('' -eq $InstallDirectory)
{
$personalModules = Join-Path -Path ([Environment]::GetFolderPath('MyDocuments')) -ChildPath WindowsPowerShell\Modules
[CmdLetBinding()]
Param (
[ValidateNotNullOrEmpty()]
[String]$ModuleName = 'ImportExcel',
[String]$InstallDirectory,
[ValidateNotNullOrEmpty()]
[String]$GitPath = 'https://raw.github.com/dfinke/ImportExcel/master'
)
if (($env:PSModulePath -split ';') -notcontains $personalModules) {
Write-Warning "$personalModules is not in `$env:PSModulePath"
Begin {
Try {
Write-Verbose "$ModuleName module installation started"
$Files = @(
'Charting.ps1',
'ConvertFromExcelData.ps1',
'ConvertFromExcelToSQLInsert.ps1',
'ConvertToExcelXlsx.ps1',
'Copy-ExcelWorkSheet.ps1',
'EPPlus.dll',
'Export-Excel.ps1',
'Export-ExcelSheet.ps1',
'Get-ExcelColumnName.ps1',
'Get-ExcelSheetInfo.ps1',
'Get-ExcelWorkbookInfo.ps1',
'Get-HtmlTable.ps1',
'Get-Range.ps1',
'Get-XYRange.ps1',
'Import-Html.ps1',
'ImportExcel.psd1',
'ImportExcel.psm1',
'InferData.ps1',
'Invoke-Sum.ps1',
'New-ConditionalFormattingIconSet.ps1',
'New-ConditionalText.ps1',
'New-ExcelChart.ps1',
'New-PSItem.ps1',
'Pivot.ps1',
'Plot.ps1',
'Set-CellStyle.ps1',
'TrackingUtils.ps1',
'Update-FirstObjectProperties.ps1'
)
}
if (!(Test-Path $personalModules)) {
Write-Error "$personalModules does not exist"
Catch {
throw "Failed installing the module in the install directory '$InstallDirectory': $_"
}
$InstallDirectory = Join-Path -Path $personalModules -ChildPath ImportExcel
}
if (!(Test-Path $InstallDirectory)) {
$null = mkdir $InstallDirectory
}
Process {
Try {
if (-not $InstallDirectory) {
Write-Verbose "$ModuleName no installation directory provided"
$wc = New-Object System.Net.WebClient
$fileList |
ForEach-Object {
$wc.DownloadFile("https://raw.github.com/dfinke/ImportExcel/master/$_","$installDirectory\$_")
$PersonalModules = Join-Path -Path ([Environment]::GetFolderPath('MyDocuments')) -ChildPath WindowsPowerShell\Modules
if (($env:PSModulePath -split ';') -notcontains $PersonalModules) {
Write-Warning "$ModuleName personal module path '$PersonalModules' not found in '`$env:PSModulePath'"
}
if (-not (Test-Path $PersonalModules)) {
Write-Error "$ModuleName path '$PersonalModules' does not exist"
}
$InstallDirectory = Join-Path -Path $PersonalModules -ChildPath $ModuleName
Write-Verbose "$ModuleName default installation directory is '$InstallDirectory'"
}
if (-not (Test-Path $InstallDirectory)) {
New-Item -Path $InstallDirectory -ItemType Directory -EA Stop | Out-Null
Write-Verbose "$ModuleName created module folder '$InstallDirectory'"
}
$WebClient = New-Object System.Net.WebClient
$Files | ForEach-Object {
$WebClient.DownloadFile("$GitPath/$_","$installDirectory\$_")
Write-Verbose "$ModuleName installed module file '$_'"
}
Write-Verbose "$ModuleName module installation successful"
}
Catch {
throw "Failed installing the module in the install directory '$InstallDirectory': $_"
}
}

View File

@@ -1,37 +1,76 @@
$ModuleName = "ImportExcel"
$ModulePath = "C:\Program Files\WindowsPowerShell\Modules"
$TargetPath = "$($ModulePath)\$($ModuleName)"
<#
.SYNOPSIS
Install the module in the PowerShell module folder.
if(!(Test-Path $TargetPath)) { md $TargetPath | out-null}
.DESCRIPTION
Install the module in the PowerShell module folder by copying all the files.
#>
$targetFiles = echo `
*.psm1 `
*.psd1 `
*.dll `
New-ConditionalText.ps1 `
New-ConditionalFormattingIconSet.ps1 `
Export-Excel.ps1 `
Export-ExcelSheet.ps1 `
New-ExcelChart.ps1 `
Invoke-Sum.ps1 `
InferData.ps1 `
Get-ExcelColumnName.ps1 `
Get-XYRange.ps1 `
Charting.ps1 `
New-PSItem.ps1 `
Pivot.ps1 `
Get-ExcelSheetInfo.ps1 `
Get-ExcelWorkbookInfo.ps1 `
New-ConditionalText.ps1 `
Get-HtmlTable.ps1 `
Import-Html.ps1 `
Get-Range.ps1 `
TrackingUtils.ps1 `
Copy-ExcelWorkSheet.ps1 `
Set-CellStyle.ps1 `
plot.ps1
[CmdLetBinding()]
Param (
[ValidateNotNullOrEmpty()]
[String]$ModuleName = 'ImportExcel',
[ValidateScript({Test-Path -Path $_ -Type Container})]
[String]$ModulePath = 'C:\Program Files\WindowsPowerShell\Modules'
)
Get-ChildItem $targetFiles |
ForEach-Object {
Copy-Item -Verbose -Path $_.FullName -Destination "$($TargetPath)\$($_.name)"
}
Begin {
Try {
Write-Verbose "$ModuleName module installation started"
$Files = @(
'*.dll',
'*.psd1',
'*.psm1',
'Charting.ps1',
'ConvertFromExcelData.ps1',
'ConvertFromExcelToSQLInsert.ps1',
'ConvertToExcelXlsx.ps1',
'Copy-ExcelWorkSheet.ps1',
'Export-Excel.ps1',
'Export-ExcelSheet.ps1',
'Get-ExcelColumnName.ps1',
'Get-ExcelSheetInfo.ps1',
'Get-ExcelWorkbookInfo.ps1',
'Get-HtmlTable.ps1',
'Get-Range.ps1',
'Get-XYRange.ps1',
'Import-Html.ps1',
'InferData.ps1',
'Invoke-Sum.ps1',
'New-ConditionalText.ps1',
'New-ConditionalFormattingIconSet.ps1',
'New-ExcelChart.ps1',
'New-PSItem.ps1',
'Pivot.ps1',
'Plot.ps1',
'Set-CellStyle.ps1',
'TrackingUtils.ps1',
'Update-FirstObjectProperties.ps1'
)
}
Catch {
throw "Failed installing the module '$ModuleName': $_"
}
}
Process {
Try {
$TargetPath = Join-Path -Path $ModulePath -ChildPath $ModuleName
if (-not (Test-Path $TargetPath)) {
New-Item -Path $TargetPath -ItemType Directory -EA Stop | Out-Null
Write-Verbose "$ModuleName created module folder '$TargetPath'"
}
Get-ChildItem $Files | ForEach-Object {
Copy-Item -Path $_.FullName -Destination "$($TargetPath)\$($_.Name)"
Write-Verbose "$ModuleName installed module file '$($_.Name)'"
}
Write-Verbose "$ModuleName module installation successful"
}
Catch {
throw "Failed installing the module '$ModuleName': $_"
}
}

149
README.md
View File

@@ -1,32 +1,119 @@
PowerShell Import-Excel
-
This PowerShell Module wraps the .NET [EPPlus DLL](http://epplus.codeplex.com/) (included). Easily integrate reading and writing Excel spreadsheets into PowerShell, without launching Excel in the background. You can also automate the creation of Pivot Tables and Charts.
This PowerShell Module allows you to read and write Excel files without installing Microsoft Excel on your system. No need to bother with the cumbersome Excel COM-objects thanks to the .NET EPPlus DLL (http://epplus.codeplex.com/) which is included in the module. Creating Tables, Pivot Tables, Charts and much more has just become a lot easier.
![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/testimonial.png)
Installation
-
#### [Powershell V5](https://www.microsoft.com/en-us/download/details.aspx?id=50395) and Later
You can install ImportExcel directly from the Powershell Gallery
#### [PowerShell V5](https://www.microsoft.com/en-us/download/details.aspx?id=50395) and Later
You can install the `ImportExcel` module directly from the PowerShell Gallery
* [Recommended] Install to your personal Powershell Modules folder
```powershell
* [Recommended] Install to your personal PowerShell Modules folder
```PowerShell
Install-Module ImportExcel -scope CurrentUser
```
* [Requires Elevation] Install for Everyone (computer Powershell Modules folder)
```powershell
* [Requires Elevation] Install for Everyone (computer PowerShell Modules folder)
```PowerShell
Install-Module ImportExcel
```
#### Powershell V4 and Earlier
#### PowerShell V4 and Earlier
To install to your personal modules folder (e.g. ~\Documents\WindowsPowerShell\Modules), run:
```powershell
```PowerShell
iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfinke/ImportExcel/master/Install.ps1')
```
# What's new
#### 10/2/2017
Thanks to [Jeremy Brun](https://github.com/jeremytbrun)
Fixed issues related to use of -Title parameter combined with column formatting parameters.
- [Issue #182](https://github.com/dfinke/ImportExcel/issues/182)
- [Issue #89](https://github.com/dfinke/ImportExcel/issues/89)
#### 9/28/2017 (Version 4.0.1)
- Added a new parameter called `Password` to import password protected files
- Added even more `Pester` tests for a more robust and bug free module
- Renamed parameter 'TopRow' to 'StartRow'
This allows us to be more concise when new parameters ('StartColumn', ..) will be added in the future Your code will not break after the update, because we added an alias for backward compatibility
Special thanks to [robinmalik](https://github.com/robinmalik) for providing us with [the code](https://github.com/dfinke/ImportExcel/issues/174) to implement this new feature. A high five to [DarkLite1](https://github.com/DarkLite1) for the implementation.
#### 9/12/2017 (Version 4.0.0)
Super thanks and hat tip to [DarkLite1](https://github.com/DarkLite1). There is now a new and improved `Import-Excel`, not only in functionality, but also improved readability, examples and more. Not only that, he's been running it in production in his company for a number of weeks!
*Added* `Update-FirstObjectProperties` Updates the first object to contain all the properties of the object with the most properties in the array. Check out the help.
***Breaking Changes***: Due to a big portion of the code that is rewritten some slightly different behavior can be expected from the `Import-Excel` function. This is especially true for importing empty Excel files with or without using the `TopRow` parameter. To make sure that your code is still valid, please check the examples in the help or the accompanying `Pester` test file.
Moving forward, we are planning to include automatic testing with the help of `Pester`, `Appveyor` and `Travis`. From now on any changes in the module will have to be accompanied by the corresponding `Pester` tests to avoid breakages of code and functionality. This is in preparation for new features coming down the road.
#### 7/3/2017
Thanks to [Mikkel Nordberg](https://www.linkedin.com/in/mikkelnordberg). He contributed a `ConvertTo-ExcelXlsx`. To use it, Excel needs to be installed. The function converts the older Excel file format ending in `.xls` to the new format ending in `.xlsx`.
#### 6/15/2017
Huge thank you to [DarkLite1](https://github.com/DarkLite1)! Refactoring of code, adding help, adding features, fixing bugs. Specifically this long outstanding one:
[Export-Excel: Numeric values not correct](https://github.com/dfinke/ImportExcel/issues/168)
It is fantastic to work with people like `DarkLite1` in the community, to help make the module so much better. A hat to you.
Another shout out to [Damian Reeves](https://twitter.com/DamReev)! His questions turn into great features. He asked if it was possible to import an Excel worksheet and transform the data into SQL `INSERT` statements. We can now answer that question with a big YES!
```PowerShell
ConvertFrom-ExcelToSQLInsert People .\testSQLGen.xlsx
```
```
INSERT INTO People ('First', 'Last', 'The Zip') Values('John', 'Doe', '12345');
INSERT INTO People ('First', 'Last', 'The Zip') Values('Jim', 'Doe', '12345');
INSERT INTO People ('First', 'Last', 'The Zip') Values('Tom', 'Doe', '12345');
INSERT INTO People ('First', 'Last', 'The Zip') Values('Harry', 'Doe', '12345');
INSERT INTO People ('First', 'Last', 'The Zip') Values('Jane', 'Doe', '12345');
```
## Bonus Points
Use the underlying `ConvertFrom-ExcelData` function and you can use a scriptblock to format the data however you want.
```PowerShell
ConvertFrom-ExcelData .\testSQLGen.xlsx {
param($propertyNames, $record)
$reportRecord = @()
foreach ($pn in $propertyNames) {
$reportRecord += "{0}: {1}" -f $pn, $record.$pn
}
$reportRecord +=""
$reportRecord -join "`r`n"
}
```
Generates
```
First: John
Last: Doe
The Zip: 12345
First: Jim
Last: Doe
The Zip: 12345
First: Tom
Last: Doe
The Zip: 12345
First: Harry
Last: Doe
The Zip: 12345
First: Jane
Last: Doe
The Zip: 12345
```
#### 2/2/2017
Thank you to [DarkLite1](https://github.com/DarkLite1) for more updates
@@ -45,22 +132,22 @@ Big thanks to [DarkLite1](https://github.com/DarkLite1) for some great updates
Get-ExcelWorkbookInfo .\Test.xlsx
CorePropertiesXml : #document
Title :
Subject :
Title :
Subject :
Author : Konica Minolta User
Comments :
Keywords :
Comments :
Keywords :
LastModifiedBy : Bond, James (London) GBR
LastPrinted : 2017-01-21T12:36:11Z
Created : 17/01/2017 13:51:32
Category :
Status :
Category :
Status :
ExtendedPropertiesXml : #document
Application : Microsoft Excel
HyperlinkBase :
HyperlinkBase :
AppVersion : 14.0300
Company : Secret Service
Manager :
Manager :
Modified : 10/02/2017 12:45:37
CustomPropertiesXml : #document
```
@@ -68,22 +155,22 @@ Big thanks to [DarkLite1](https://github.com/DarkLite1) for some great updates
#### 12/22/2016
- Added `-Now` switch. This short cuts the process, automatically creating a temp file and enables the `-Show`, `-AutoFilter`, `-AutoSize` switches.
```powershell
```PowerShell
Get-Process | Select Company, Handles | Export-Excel -Now
```
- Added ScriptBlocks for coloring cells. Check out [Examples](https://github.com/dfinke/ImportExcel/tree/master/Examples/FormatCellStyles)
```powershell
```PowerShell
Get-Process |
Select-Object Company,Handles,PM, NPM|
Select-Object Company,Handles,PM, NPM|
Export-Excel $xlfile -Show -AutoSize -CellStyleSB {
param(
$workSheet,
$totalRows,
$lastColumn
)
Set-CellStyle $workSheet 1 $LastColumn Solid Cyan
foreach($row in (2..$totalRows | Where-Object {$_ % 2 -eq 0})) {
@@ -98,7 +185,7 @@ Get-Process |
![](https://github.com/dfinke/ImportExcel/blob/master/images/CellFormatting.png?raw=true)
#### 9/28/2016
[Fixed](https://github.com/dfinke/ImportExcel/pull/126) Powershell 3.0 compatibility. Thanks to [headsphere](https://github.com/headsphere). He used `$obj.PSObject.Methods[$target]` snytax to make it backward compatible. PS v4.0 and later allow `$obj.$target`.
[Fixed](https://github.com/dfinke/ImportExcel/pull/126) PowerShell 3.0 compatibility. Thanks to [headsphere](https://github.com/headsphere). He used `$obj.PSObject.Methods[$target]` snytax to make it backward compatible. PS v4.0 and later allow `$obj.$target`.
Thank you to [xelsirko](https://github.com/xelsirko) for fixing - *Import-module importexcel gives version warning if started inside background job*
@@ -140,7 +227,7 @@ Huge thank you to [Willie Möller](https://github.com/W1M0R)
#### 4/18/2016
Thanks to [Paul Williams](https://github.com/pauldalewilliams) for this feature. Now data can be transposed to columns for better charting.
```powershell
```PowerShell
$file = "C:\Temp\ps.xlsx"
rm $file -ErrorAction Ignore
@@ -159,7 +246,7 @@ ps |
Add `-PivotDataToColumn`
```powershell
```PowerShell
$file = "C:\Temp\ps.xlsx"
rm $file -ErrorAction Ignore
@@ -236,7 +323,7 @@ $data |
#### 3/2/2016
* Added `GreaterThan`, `GreaterThanOrEqual`, `LessThan`, `LessThanOrEqual` to `New-ConditionalText`
```powershell
```PowerShell
echo 489 668 299 777 860 151 119 497 234 788 |
Export-Excel c:\temp\test.xlsx -Show `
-ConditionalText (New-ConditionalText -ConditionalType GreaterThan 525)
@@ -244,7 +331,7 @@ echo 489 668 299 777 860 151 119 497 234 788 |
![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/GTConditional.png)
#### 2/22/2016
* `Import-Html` using Lee Holmes [Extracting Tables from PowerShells Invoke-WebRequest](http://www.leeholmes.com/blog/2015/01/05/extracting-tables-from-powershells-invoke-webrequest/)
* `Import-Html` using Lee Holmes [Extracting Tables from PowerShells Invoke-WebRequest](http://www.leeholmes.com/blog/2015/01/05/extracting-tables-from-PowerShells-invoke-webrequest/)
![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/ImportHtml.gif)
@@ -254,7 +341,7 @@ echo 489 668 299 777 860 151 119 497 234 788 |
## Try *PassThru*
```powershell
```PowerShell
$file = "C:\Temp\passthru.xlsx"
rm $file -ErrorAction Ignore
@@ -317,14 +404,14 @@ Stay tuned for a [blog post](http://www.dougfinke.com/blog/) and examples.
Big bug fix for version 3.0 PowerShell folks!
This technique fails in 3.0 and works in 4.0 and later.
```powershell
```PowerShell
$m="substring"
"hello".$m(2,1)
```
Adding `.invoke` works in 3.0 and later.
```powershell
```PowerShell
$m="substring"
"hello".$m.invoke(2,1)
```
@@ -396,7 +483,7 @@ Or, check out the short ***"How To"*** video.
#### 7/09/2015
* For -PivotRows you can pass a `hashtable` with the name of the property and the type of calculation. `Sum`, `Average`, `Max`, `Min`, `Product`, `StdDev`, `StdDevp`, `Var`, `Varp`
```powershell
```PowerShell
Get-Service |
Export-Excel "c:\temp\test.xlsx" `
-Show `
@@ -529,7 +616,7 @@ You can set the pattern, size and of if the title is bold.
Handles = {$p|select company, handles}
Services = {gsv}
Files = {dir -File}
Albums = {(Invoke-RestMethod http://www.dougfinke.com/powershellfordevelopers/albums.js)}
Albums = {(Invoke-RestMethod http://www.dougfinke.com/PowerShellfordevelopers/albums.js)}
}
Export-MultipleExcelSheets -Show -AutoSize .\testExport.xlsx $DataToGather

View File

@@ -0,0 +1,92 @@
Function Update-FirstObjectProperties {
<#
.SYNOPSIS
Updates the first object to contain all the properties of the object with the most properties in the array.
.DESCRIPTION
Updates the first object to contain all the properties of the object with the most properties in the array. This is usefull when not all objects have the same quantity of properties and CmdLets like Out-GridView or Export-Excel are not able to show all the properties because the first object doesn't have them all.
.EXAMPLE
$Array = @()
$Obj1 = [PSCustomObject]@{
Member1 = 'First'
Member2 = 'Second'
}
$Obj2 = [PSCustomObject]@{
Member1 = 'First'
Member2 = 'Second'
Member3 = 'Third'
}
$Obj3 = [PSCustomObject]@{
Member1 = 'First'
Member2 = 'Second'
Member3 = 'Third'
Member4 = 'Fourth'
}
$Array = $Obj1, $Obj2, $Obj3
$Array | Out-GridView -Title 'Not showing Member3 and Member4'
$Array | Update-FirstObjectProperties | Out-GridView -Title 'All properties are visible'
Updates the fist object of the array by adding Member3 and Member4.
.EXAMPLE
$ExcelParams = @{
Path = $env:TEMP + '\Excel.xlsx'
Show = $true
Verbose = $true
}
Remove-Item -Path $ExcelParams.Path -Force -EA Ignore
$Array = @()
$Obj1 = [PSCustomObject]@{
Member1 = 'First'
Member2 = 'Second'
}
$Obj2 = [PSCustomObject]@{
Member1 = 'First'
Member2 = 'Second'
Member3 = 'Third'
}
$Obj3 = [PSCustomObject]@{
Member1 = 'First'
Member2 = 'Second'
Member3 = 'Third'
Member4 = 'Fourth'
}
$Array = $Obj1, $Obj2, $Obj3
$Array | Out-GridView -Title 'Not showing Member3 and Member4'
$Array | Update-FirstObjectProperties | Export-Excel @ExcelParams -WorkSheetname Numbers
Updates the first object of the array by adding property 'Member3' and 'Member4'. Afterwards. all objects are exported to an Excel file and all column headers are visible.
.LINK
https://github.com/dfinke/ImportExcel
.NOTES
CHANGELOG
2017/06/08 Function born #>
Try {
$Union = @()
$Input | ForEach-Object {
If ($Union.Count) {
$_ | Get-Member | Where {-not ($Union[0] | Get-Member $_.Name)} | ForEach-Object {
$Union[0] | Add-Member -MemberType NoteProperty -Name $_.Name -Value $Null
}
}
$Union += $_
}
$Union
}
Catch {
throw "Failed updating the properties of the first object: $_"
}
}