mirror of
https://github.com/dfinke/ImportExcel.git
synced 2025-12-06 00:23:20 +00:00
Tidying of case, parameter clarity, removal of aliasess.
Added timeout to send-SqlDataToExcel Added Merge WorkSheet Fixed bugs in Compare-Worksheet
This commit is contained in:
@@ -85,11 +85,12 @@
|
||||
[switch]$StrikeThru
|
||||
)
|
||||
#Allow add conditional formatting to work like Set-Format (with single ADDRESS parameter) split it to get worksheet and Range of cells.
|
||||
If ($Address -and -not $WorkSheet -and -not $Range) {
|
||||
if ($Address -and -not $WorkSheet -and -not $Range) {
|
||||
$WorkSheet = $Address.Worksheet[0]
|
||||
$Range = $Address.Address
|
||||
}
|
||||
If ($ThreeIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddThreeIconSet($Range , $ThreeIconsSet)}
|
||||
}
|
||||
if ($rule -eq "Databar" -and -not $databarColor) {Write-Warning -Message "-DatabarColor must be specified for the Databar rule type" }
|
||||
if ( $ThreeIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddThreeIconSet($Range , $ThreeIconsSet)}
|
||||
elseif ($FourIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddFourIconSet( $Range , $FourIconsSet) }
|
||||
elseif ($FiveIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddFiveIconSet( $Range , $IconType) }
|
||||
elseif ($DataBarColor) {$rule = $WorkSheet.ConditionalFormatting.AddDatabar( $Range , $DataBarColor) }
|
||||
|
||||
@@ -14,6 +14,10 @@ if (Get-Command -Name register-argumentCompleter -ErrorAction SilentlyContinue)
|
||||
Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName FontColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName TabColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Merge-Worksheet -ParameterName AddBackgroundColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Merge-Worksheet -ParameterName ChangeBackgroundColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Merge-Worksheet ` -ParameterName DeleteBackgroundColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Merge-Worksheet -ParameterName KeyFontColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Set-Format -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Set-Format -ParameterName FontColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Set-Format -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ipmo C:\Users\mcp\Documents\GitHub\ImportExcel\ImportExcel.psd1 -Force -Verbose
|
||||
Import-Module -name ImportExcel -Force -Verbose
|
||||
|
||||
$sql = @"
|
||||
SELECT rootfile.baseName , rootfile.extension , Image.fileWidth AS width , image.fileHeight AS height ,
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
.PARAMETER TargetData
|
||||
Data to insert onto the worksheet - this is often provided from the pipeline.
|
||||
.PARAMETER ExcludeProperty
|
||||
Speficies properties which may exist in the target data but should not be placed on the worksheet
|
||||
Specifies properties which may exist in the target data but should not be placed on the worksheet
|
||||
.PARAMETER Title
|
||||
Text of a title to be placed in Cell A1
|
||||
.PARAMETER TitleBold
|
||||
@@ -32,9 +32,9 @@
|
||||
.PARAMETER IncludePivotTable
|
||||
Adds a Pivot table using the data in the worksheet
|
||||
.PARAMETER PivotRows
|
||||
Name(s) columns from the spreadhseet which will prvoide the row name(s) in the pivot table
|
||||
Name(s) columns from the spreadhseet which will provide the row name(s) in the pivot table
|
||||
.PARAMETER PivotColumns
|
||||
Name(s) columns from the spreadhseet which will prvoide the Column name(s) in the pivot table
|
||||
Name(s) columns from the spreadhseet which will provide the Column name(s) in the pivot table
|
||||
.PARAMETER PivotData
|
||||
Hash table in the form ColumnName = Average|Count|CountNums|Max|Min|Product|None|StdDev|StdDevP|Sum|Var|VarP to provide the data in the Pivot table
|
||||
.PARAMETER PivotTableDefinition,
|
||||
@@ -62,13 +62,13 @@
|
||||
.PARAMETER TableStyle
|
||||
Selects the style for the named table - defaults to 'Medium6'
|
||||
.PARAMETER ExcelChartDefinition
|
||||
A hash table containing ChartType, Title, NoLegend, ShowCategory, ShowPecent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts
|
||||
A hash table containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts
|
||||
.PARAMETER HideSheet
|
||||
Name(s) of Sheet(s) to hide in the workbook
|
||||
.PARAMETER KillExcel
|
||||
Closes Excel - prevents errors writing to the file because Excel has it open
|
||||
.PARAMETER AutoNameRange
|
||||
Makes each column a named range
|
||||
Makes each column a named range
|
||||
.PARAMETER StartRow
|
||||
Row to start adding data. 1 by default. Row 1 will contain the title if any. Then headers will appear (Unless -No header is specified) then the data appears
|
||||
.PARAMETER StartColumn
|
||||
@@ -1056,4 +1056,4 @@ function New-PivotTableDefinition {
|
||||
$parameters.Remove('PivotTableName')
|
||||
|
||||
@{$PivotTableName = $parameters}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ function Export-ExcelSheet {
|
||||
$xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path
|
||||
$workbook = $xl.Workbook
|
||||
|
||||
$targetSheets = $workbook.Worksheets | Where {$_.Name -Match $SheetName}
|
||||
$targetSheets = $workbook.Worksheets | Where-Object {$_.Name -Match $SheetName}
|
||||
|
||||
$params = @{} + $PSBoundParameters
|
||||
$params.Remove("OutputPath")
|
||||
|
||||
@@ -14,7 +14,7 @@ Param (
|
||||
#Path to the Excel file whose chars we will export.
|
||||
$Path = "C:\Users\public\Documents\stats.xlsx",
|
||||
#If specified, output file objects representing the image files.
|
||||
[switch]$passthru,
|
||||
[switch]$Passthru,
|
||||
#Format to write - JPG by default
|
||||
[ValidateSet("JPG","PNG","GIF")]
|
||||
$OutputType = "JPG",
|
||||
@@ -23,15 +23,14 @@ Param (
|
||||
)
|
||||
|
||||
#if no output folder was specified, set destination to the folder where the Excel file came from
|
||||
if (-not $Destination) {$Destination = Split-Path -Path $path -Parent }
|
||||
if (-not $Destination) {$Destination = Split-Path -Path $Path -Parent }
|
||||
|
||||
#Call up Excel and tell it to open the file.
|
||||
try { $excelApp = New-Object -ComObject "Excel.Application" }
|
||||
catch { Write-Warning "Could not start Excel application - which usually means it is not installed." ; return }
|
||||
|
||||
try { $excelWorkBook = $excelApp.Workbooks.Open($path) }
|
||||
catch { Write-Warning "Could not start Excel application - which usually means it is not installed." ; return }
|
||||
|
||||
try { $excelWorkBook = $excelApp.Workbooks.Open($Path) }
|
||||
catch { Write-Warning -Message "Could not Open $Path." ; return }
|
||||
|
||||
#For each worksheet, for each chart, jump to the chart, create a filename of "WorksheetName_ChartTitle.jpg", and export the file.
|
||||
foreach ($excelWorkSheet in $excelWorkBook.Worksheets) {
|
||||
@@ -41,11 +40,12 @@ foreach ($excelWorkSheet in $excelWorkBook.Worksheets) {
|
||||
$excelApp.Goto($excelchart.TopLeftCell,$true)
|
||||
$imagePath = Join-Path -Path $Destination -ChildPath ($excelWorkSheet.Name + "_" + ($excelchart.Chart.ChartTitle.Text -split "\s\d\d:\d\d,")[0] + ".$OutputType")
|
||||
if ( $excelchart.Chart.Export($imagePath, $OutputType, $false) ) { # Export returs true/false for success/failure
|
||||
if ($passThru) {Get-Item -Path $imagePath } # when succesful return a file object (-passthru) or print a verbose message, write warning for any failures
|
||||
if ($Passthru) {Get-Item -Path $imagePath } # when succesful return a file object (-Passthru) or print a verbose message, write warning for any failures
|
||||
else {Write-Verbose -Message "Exported $imagePath"}
|
||||
}
|
||||
else {Write-Warning -Message "Failure exporting $imagePath" }
|
||||
}
|
||||
}
|
||||
|
||||
$excelApp.Quit()
|
||||
$excelApp.DisplayAlerts = $false
|
||||
$excelWorkBook.Close($false,$null,$null)
|
||||
$excelApp.Quit()
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
RootModule = 'ImportExcel.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '4.0.13'
|
||||
ModuleVersion = '4.0.14'
|
||||
|
||||
# ID used to uniquely identify this module
|
||||
GUID = '60dd4136-feff-401a-ba27-a84458c57ede'
|
||||
|
||||
555
ImportExcel.psm1
555
ImportExcel.psm1
@@ -1,241 +1,243 @@
|
||||
Add-Type -Path "$($PSScriptRoot)\EPPlus.dll"
|
||||
|
||||
. $PSScriptRoot\AddConditionalFormatting.ps1
|
||||
. $PSScriptRoot\Charting.ps1
|
||||
. $PSScriptRoot\ColorCompletion.ps1
|
||||
. $PSScriptRoot\ConvertExcelToImageFile.ps1
|
||||
. $PSScriptRoot\Compare-WorkSheet.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\Get-Range.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\Open-ExcelPackage.ps1
|
||||
. $PSScriptRoot\Pivot.ps1
|
||||
. $PSScriptRoot\Send-SQLDataToExcel.ps1
|
||||
. $PSScriptRoot\Set-CellStyle.ps1
|
||||
. $PSScriptRoot\Set-Column.ps1
|
||||
. $PSScriptRoot\Set-Row.ps1
|
||||
. $PSScriptRoot\SetFormat.ps1
|
||||
. $PSScriptRoot\TrackingUtils.ps1
|
||||
. $PSScriptRoot\Update-FirstObjectProperties.ps1
|
||||
#region import everything we need
|
||||
Add-Type -Path "$($PSScriptRoot)\EPPlus.dll"
|
||||
. $PSScriptRoot\AddConditionalFormatting.ps1
|
||||
. $PSScriptRoot\Charting.ps1
|
||||
. $PSScriptRoot\ColorCompletion.ps1
|
||||
. $PSScriptRoot\ConvertExcelToImageFile.ps1
|
||||
. $PSScriptRoot\Compare-WorkSheet.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\Get-Range.ps1
|
||||
. $PSScriptRoot\Get-XYRange.ps1
|
||||
. $PSScriptRoot\Import-Html.ps1
|
||||
. $PSScriptRoot\InferData.ps1
|
||||
. $PSScriptRoot\Invoke-Sum.ps1
|
||||
. $PSScriptRoot\Merge-Worksheet.ps1
|
||||
. $PSScriptRoot\New-ConditionalFormattingIconSet.ps1
|
||||
. $PSScriptRoot\New-ConditionalText.ps1
|
||||
. $PSScriptRoot\New-ExcelChart.ps1
|
||||
. $PSScriptRoot\New-PSItem.ps1
|
||||
. $PSScriptRoot\Open-ExcelPackage.ps1
|
||||
. $PSScriptRoot\Pivot.ps1
|
||||
. $PSScriptRoot\Send-SQLDataToExcel.ps1
|
||||
. $PSScriptRoot\Set-CellStyle.ps1
|
||||
. $PSScriptRoot\Set-Column.ps1
|
||||
. $PSScriptRoot\Set-Row.ps1
|
||||
. $PSScriptRoot\SetFormat.ps1
|
||||
. $PSScriptRoot\TrackingUtils.ps1
|
||||
. $PSScriptRoot\Update-FirstObjectProperties.ps1
|
||||
|
||||
|
||||
New-Alias -Name Use-ExcelData -Value "ConvertFrom-ExcelData" -Force
|
||||
New-Alias -Name Use-ExcelData -Value "ConvertFrom-ExcelData" -Force
|
||||
|
||||
if ($PSVersionTable.PSVersion.Major -ge 5) {
|
||||
. $PSScriptRoot\Plot.ps1
|
||||
if ($PSVersionTable.PSVersion.Major -ge 5) {
|
||||
. $PSScriptRoot\Plot.ps1
|
||||
|
||||
Function New-Plot {
|
||||
Param()
|
||||
Function New-Plot {
|
||||
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'
|
||||
}
|
||||
#endregion
|
||||
Function Import-Excel {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Create custom objects from the rows in an Excel worksheet.
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Create custom objects from the rows in an Excel worksheet.
|
||||
|
||||
.DESCRIPTION
|
||||
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 ‘EPPLus.dll’.
|
||||
.DESCRIPTION
|
||||
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 ‘EPPLus.dll’.
|
||||
|
||||
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.
|
||||
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 ‘as is’, the parameter ‘-NoHeader’ can be used. In case you want to provide your own property names, you can use the parameter ‘-HeaderName’.
|
||||
If the default behavior is not desired and you want to import the complete worksheet ‘as is’, the parameter ‘-NoHeader’ can be used. In case you want to provide your own property names, you can use the parameter ‘-HeaderName’.
|
||||
|
||||
.PARAMETER Path
|
||||
Specifies the path to the Excel file.
|
||||
.PARAMETER Path
|
||||
Specifies the path to the Excel file.
|
||||
|
||||
.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 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
|
||||
Import only rows and columns that contain data, empty rows and empty columns are not imported.
|
||||
.PARAMETER DataOnly
|
||||
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.
|
||||
.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 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.
|
||||
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.
|
||||
.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 ‘as is’ and are not concerned with the property names.
|
||||
This switch is best used when you want to import the complete worksheet ‘as is’ 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.
|
||||
.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 ‘-NoHeader’ and ‘-HeaderName’ 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.
|
||||
When the parameters ‘-NoHeader’ and ‘-HeaderName’ 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.
|
||||
.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’t have a column header (usually in row 1 when ‘-StartRow’ 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 ‘as is’ by using the ‘-NoHeader’ 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 column names defined in the first row. In case a column doesn’t have a column header (usually in row 1 when ‘-StartRow’ is not used), then the unnamed columns will be skipped and the data in those columns will not be imported.
|
||||
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 ‘-HeaderName’. 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: Actors |
|
||||
----------------------------------------------
|
||||
| A B C |
|
||||
|1 First Name Address |
|
||||
|2 Chuck Norris California |
|
||||
|3 Jean-Claude Vandamme Brussels |
|
||||
----------------------------------------------
|
||||
----------------------------------------------------------
|
||||
| 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 Actors
|
||||
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Movies -HeaderName 'Movie name', 'Year', 'Rating', 'Genre'
|
||||
|
||||
First Name: Chuck
|
||||
Address : California
|
||||
Movie name: The Bodyguard
|
||||
Year : 1992
|
||||
Rating : 9
|
||||
Genre :
|
||||
|
||||
First Name: Jean-Claude
|
||||
Address : Brussels
|
||||
Movie name: The Matrix
|
||||
Year : 1999
|
||||
Rating : 8
|
||||
Genre :
|
||||
|
||||
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.
|
||||
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 the complete Excel worksheet ‘as is’ by using the ‘-NoHeader’ switch. One object is created for each row. The property names of the objects will be automatically generated (P1, P2, P3, ..).
|
||||
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 ‘-NoHeader’ (P1, P@, P#, ..). The switch ‘-DataOnly’ will speed up the import because empty rows and empty columns are not imported.
|
||||
|
||||
----------------------------------------------
|
||||
| File: Movies.xlsx - Sheet: Actors |
|
||||
----------------------------------------------
|
||||
| A B C |
|
||||
|1 First Name Address |
|
||||
|2 Chuck Norris California |
|
||||
|3 Jean-Claude Vandamme Brussels |
|
||||
----------------------------------------------
|
||||
----------------------------------------------------------
|
||||
| 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 Actors -NoHeader
|
||||
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Movies –NoHeader -DataOnly
|
||||
|
||||
P1: First Name
|
||||
P2:
|
||||
P3: Address
|
||||
P1: The Bodyguard
|
||||
P2: 1992
|
||||
P3: 9
|
||||
|
||||
P1: Chuck
|
||||
P2: Norris
|
||||
P3: California
|
||||
P1: The Matrix
|
||||
P2: 1999
|
||||
P3: 8
|
||||
|
||||
P1: Jean-Claude
|
||||
P2: Vandamme
|
||||
P3: Brussels
|
||||
P1: Skyfall
|
||||
P2: 2012
|
||||
P3: 9
|
||||
|
||||
Notice that the column header (row 1) is imported as an object too.
|
||||
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 of the objects consist of the names defined in the parameter ‘-HeaderName’. 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.
|
||||
.EXAMPLE
|
||||
Import data from an Excel worksheet. One object is created for each row. The property names are provided with the ‘-HeaderName’ parameter. The import will start from row 2 and empty columns and rows 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 |
|
||||
----------------------------------------------------------
|
||||
----------------------------------------------------------
|
||||
| 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 Movies -HeaderName 'Movie name', 'Year', 'Rating', 'Genre'
|
||||
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors -DataOnly -HeaderName 'FirstName', 'SecondName', 'City' –StartRow 2
|
||||
|
||||
Movie name: The Bodyguard
|
||||
Year : 1992
|
||||
Rating : 9
|
||||
Genre :
|
||||
FirstName : Jean-Claude
|
||||
SecondName: Vandamme
|
||||
City : Brussels
|
||||
|
||||
Movie name: The Matrix
|
||||
Year : 1999
|
||||
Rating : 8
|
||||
Genre :
|
||||
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 ‘Chuck Norris’ has not been imported, because we started the import from row 2 with the parameter ‘-StartRow 2’.
|
||||
|
||||
Movie name:
|
||||
Year :
|
||||
Rating :
|
||||
Genre :
|
||||
.LINK
|
||||
https://github.com/dfinke/ImportExcel
|
||||
|
||||
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 ‘-NoHeader’ (P1, P@, P#, ..). The switch ‘-DataOnly’ 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 –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 ‘-HeaderName’ 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' –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 ‘Chuck Norris’ has not been imported, because we started the import from row 2 with the parameter ‘-StartRow 2’.
|
||||
|
||||
.LINK
|
||||
https://github.com/dfinke/ImportExcel
|
||||
|
||||
.NOTES
|
||||
#>
|
||||
.NOTES
|
||||
#>
|
||||
|
||||
[CmdLetBinding(DefaultParameterSetName)]
|
||||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")]
|
||||
Param (
|
||||
[Alias('FullName')]
|
||||
[Parameter(ValueFromPipelineByPropertyName, ValueFromPipeline, Position=0, Mandatory)]
|
||||
@@ -251,34 +253,13 @@ Function Import-Excel {
|
||||
[Switch]$NoHeader,
|
||||
[Alias('HeaderRow','TopRow')]
|
||||
[ValidateRange(1, 9999)]
|
||||
[Int]$StartRow,
|
||||
[Int]$StartRow = 1,
|
||||
[Switch]$DataOnly,
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[String]$Password
|
||||
)
|
||||
|
||||
Begin {
|
||||
Function Add-Property {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Add the property name and value to the hashtable that will create a new object for each row.
|
||||
#>
|
||||
|
||||
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'"
|
||||
}
|
||||
Catch {
|
||||
throw "Failed adding the property name '$Name' with value '$Value': $_"
|
||||
}
|
||||
}
|
||||
|
||||
$sw = [System.Diagnostics.Stopwatch]::StartNew()
|
||||
Function Get-PropertyNames {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
@@ -313,7 +294,7 @@ Function Import-Excel {
|
||||
}
|
||||
|
||||
foreach ($C in $Columns) {
|
||||
$Worksheet.Cells[$StartRow,$C] | where {$_.Value} | Select-Object @{N='Column'; E={$C}}, Value
|
||||
$Worksheet.Cells[$StartRow,$C] | Where-Object {$_.Value} | Select-Object @{N='Column'; E={$C}}, Value
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -328,24 +309,24 @@ Function Import-Excel {
|
||||
#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."
|
||||
}
|
||||
|
||||
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])) {
|
||||
@@ -356,74 +337,59 @@ Function Import-Excel {
|
||||
$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'"
|
||||
Write-Debug $sw.Elapsed.TotalMilliseconds
|
||||
#region Get rows and columns
|
||||
#If we are doing dataonly it is quicker to work out which rows to ignore before processing the cells.
|
||||
if ($DataOnly) {
|
||||
#If we are using headers startrow will be the headerrow so examine data from startRow + 1,
|
||||
if ($NoHeader) {$range = "A" + ($StartRow ) + ":" + $Worksheet.Dimension.End.Address }
|
||||
else {$range = "A" + ($StartRow + 1 ) + ":" + $Worksheet.Dimension.End.Address }
|
||||
#We're going to look at every cell and build 2 hash tables holding rows & columns which contain data.
|
||||
#Want to Avoid 'select unique' operations & large Sorts, becuse time time taken increases with square
|
||||
#of number of items (PS uses heapsort at large size). Instead keep a list of what we have seen,
|
||||
#using Hash tables: "we've seen it" is all we need, no need to worry about "seen it before" / "Seen it many times"
|
||||
$colHash = @{}
|
||||
$rowHash = @{}
|
||||
foreach ($cell in $Worksheet.Cells[$range]) {
|
||||
if ($cell.Value -ne $null) {$colHash[$cell.Start.Column]=1; $rowHash[$cell.Start.row]=1 }
|
||||
}
|
||||
$rows = ($StartRow..($Worksheet.Dimension.End.Row)).Where({$rowHash[$_]})
|
||||
$columns = (1..($Worksheet.Dimension.End.Column) ).Where({$colHash[$_]})
|
||||
}
|
||||
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
|
||||
}
|
||||
$Columns = ($Worksheet.Dimension.Start.Column)..($Worksheet.Dimension.End.Column)
|
||||
if ($NoHeader) {$Rows = ( $StartRow)..($Worksheet.Dimension.End.Row) }
|
||||
else {$Rows = (1 + $StartRow)..($Worksheet.Dimension.End.Row) }
|
||||
}
|
||||
#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-Object 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
|
||||
Write-Debug $sw.Elapsed.TotalMilliseconds
|
||||
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) {
|
||||
$NewRow[$P.Value] = $Worksheet.Cells[$R, $P.Column].Value
|
||||
Write-Verbose "Import cell '$($Worksheet.Cells[$R, $P.Column].Address)' with property name '$($p.Value)' and value '$($Worksheet.Cells[$R, $P.Column].Value)'."
|
||||
}
|
||||
|
||||
[PSCustomObject]$NewRow
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
Write-Debug $sw.Elapsed.TotalMilliseconds
|
||||
}
|
||||
Catch {
|
||||
throw "Failed importing the Excel workbook '$Path' with worksheet '$Worksheetname': $_"
|
||||
@@ -499,7 +465,7 @@ function ConvertFrom-ExcelSheet {
|
||||
$xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $stream
|
||||
$workbook = $xl.Workbook
|
||||
|
||||
$targetSheets = $workbook.Worksheets | Where {$_.Name -like $SheetName}
|
||||
$targetSheets = $workbook.Worksheets | Where-Object {$_.Name -like $SheetName}
|
||||
|
||||
$params = @{} + $PSBoundParameters
|
||||
$params.Remove("OutputPath")
|
||||
@@ -518,10 +484,11 @@ function ConvertFrom-ExcelSheet {
|
||||
|
||||
$stream.Close()
|
||||
$stream.Dispose()
|
||||
$xl.Dispose()
|
||||
$xl.Dispose()
|
||||
}
|
||||
|
||||
function Export-MultipleExcelSheets {
|
||||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")]
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
$Path,
|
||||
|
||||
@@ -25,6 +25,7 @@ Begin {
|
||||
'AddConditionalFormatting.ps1',
|
||||
'Charting.ps1',
|
||||
'ColorCompletion.ps1',
|
||||
'Compare-Worksheet.ps1',
|
||||
'ConvertFromExcelData.ps1',
|
||||
'ConvertFromExcelToSQLInsert.ps1',
|
||||
'ConvertExcelToImageFile.ps1',
|
||||
|
||||
275
Merge-worksheet.ps1
Normal file
275
Merge-worksheet.ps1
Normal file
@@ -0,0 +1,275 @@
|
||||
Function Merge-Worksheet {
|
||||
<#
|
||||
.Synopsis
|
||||
Merges two worksheets (or other objects) into a single worksheet with differences marked up.
|
||||
.Description
|
||||
The Compare-Worksheet command takes two worksheets and marks differences in the source document, and optionally outputs a grid showing the changes.
|
||||
By contrast the Merge-Worksheet command takes the worksheets and combines them into a single sheet showing the old and new data side by side .
|
||||
Although it is designed to work with Excel data it can work with arrays of any kind of object; so it can be a merge *of* worksheets, or a merge *to* worksheet.
|
||||
.Example
|
||||
merge-worksheet "Server54.xlsx" "Server55.xlsx" -WorkSheetName services -OutputFile Services.xlsx -OutputSheetName 54-55 -show
|
||||
The workbooks contain audit information for two servers, one page contains a list of services. This command creates a worksheet named 54-55
|
||||
in a workbook named services and shows all the services and their differences, and opens it in Excel
|
||||
.Example
|
||||
merge-worksheet "Server54.xlsx" "Server55.xlsx" -WorkSheetName services -OutputFile Services.xlsx -OutputSheetName 54-55 -HideEqual -AddBackgroundColor LightBlue -show
|
||||
This modifies the previous command to hide the equal rows in the output sheet and changes the color used to mark rows "Added" to the second file.
|
||||
.Example
|
||||
merge-worksheet -OutputFile .\j1.xlsx -OutputSheetName test11 -ReferenceObject (dir .\ImportExcel\4.0.7) -DifferenceObject (dir '\ImportExcel\4.0.8') -Property Length -Show
|
||||
This version compares two directories, and marks what has changed.
|
||||
Because no "Key" property is given, "Name" is assumed to be the key and the only other property examined is length.
|
||||
Files which are added or deleted or have changedd size will be highlighed in the output sheet. Changes to dates or other attributes will be ignored
|
||||
.Example
|
||||
merge-worksheet -Outf .\dummy.xlsx -RefO (dir .\ImportExcel\4.0.7) -DiffO (dir .\ImportExcel\4.0.8') -Pr Length -WhatIf -Passthru | Out-GridView
|
||||
This time no file is written because -WhatIf is specified, and -Passthru causes the results to go Out-Gridview. This version uses aliases to shorten the parameters,
|
||||
(OutputFileName can be "outFile" and the sheet "OutSheet" : DifferenceObject & RefeenceObject can be DiffObject & RefObject)
|
||||
#>
|
||||
[cmdletbinding(SupportsShouldProcess=$true)]
|
||||
Param(
|
||||
#First Excel file to compare. You can compare two Excel files or two other objects but not one of each.
|
||||
[parameter(ParameterSetName='A',Mandatory=$true,Position=0)]
|
||||
[parameter(ParameterSetName='B',Mandatory=$true,Position=0)]
|
||||
[parameter(ParameterSetName='C',Mandatory=$true,Position=0)]
|
||||
$Referencefile ,
|
||||
|
||||
#Second Excel file to compare.
|
||||
[parameter(ParameterSetName='A',Mandatory=$true,Position=1)]
|
||||
[parameter(ParameterSetName='B',Mandatory=$true,Position=1)]
|
||||
[parameter(ParameterSetName='C',Mandatory=$true,Position=1)]
|
||||
[parameter(ParameterSetName='E',Mandatory=$true,Position=1)]
|
||||
$Differencefile ,
|
||||
|
||||
#Name(s) of worksheets to compare,
|
||||
[parameter(ParameterSetName='A',Position=2)]
|
||||
[parameter(ParameterSetName='B',Position=2)]
|
||||
[parameter(ParameterSetName='C',Position=2)]
|
||||
[parameter(ParameterSetName='E',Position=2)]
|
||||
$WorkSheetName = "Sheet1",
|
||||
|
||||
#The row from where we start to import data, all rows above the StartRow are disregarded. By default this is the first row.
|
||||
[parameter(ParameterSetName='A')]
|
||||
[parameter(ParameterSetName='B')]
|
||||
[parameter(ParameterSetName='C')]
|
||||
[parameter(ParameterSetName='E')]
|
||||
[int]$Startrow = 1,
|
||||
|
||||
#Specifies custom property names to use, instead of the values defined in the column headers of the TopRow.
|
||||
[Parameter(ParameterSetName='B',Mandatory=$true)]
|
||||
[String[]]$Headername,
|
||||
|
||||
#Automatically generate property names (P1, P2, P3, ..) instead of the using the values the top row of the sheet.
|
||||
[Parameter(ParameterSetName='C',Mandatory=$true)]
|
||||
[switch]$NoHeader,
|
||||
|
||||
[parameter(ParameterSetName='D',Mandatory=$true)]
|
||||
[parameter(ParameterSetName='E',Mandatory=$true)]
|
||||
[Alias('RefObject')]
|
||||
$ReferenceObject ,
|
||||
[parameter(ParameterSetName='D',Mandatory=$true,Position=1)]
|
||||
[Alias('DiffObject')]
|
||||
$DifferenceObject ,
|
||||
[parameter(ParameterSetName='D',Position=2)]
|
||||
$DiffPrefix = "=>" ,
|
||||
#File to hold merged data.
|
||||
[parameter(Position=3)]
|
||||
[Alias('OutFile')]
|
||||
$OutputFile ,
|
||||
#Name of worksheet to output - if none specified will use the reference worksheet name.
|
||||
[parameter(Position=4)]
|
||||
[Alias('OutSheet')]
|
||||
$OutputSheetName = "Sheet1",
|
||||
#Properties to include in the DIFF - supports wildcards, default is "*".
|
||||
$Property = "*" ,
|
||||
#Properties to exclude from the the search - supports wildcards.
|
||||
$ExcludeProperty ,
|
||||
#Name of a column which is unique used to pair up rows from the refence and difference side, default is "Name".
|
||||
$Key = "Name" ,
|
||||
#Sets the font color for the "key" field; this means you can filter by color to get only changed rows.
|
||||
[System.Drawing.Color]$KeyFontColor = "DarkRed",
|
||||
#Sets the background color for changed rows.
|
||||
[System.Drawing.Color]$ChangeBackgroundColor = "Orange",
|
||||
#Sets the background color for rows in the reference but deleted from the difference sheet.
|
||||
[System.Drawing.Color]$DeleteBackgroundColor = "LightPink",
|
||||
#Sets the background color for rows not in the reference but added to the difference sheet.
|
||||
[System.Drawing.Color]$AddBackgroundColor = "PaleGreen",
|
||||
#if Specified hides the rows in the spreadsheet that are equal and only shows changes, added or deleted rows.
|
||||
[switch]$HideEqual ,
|
||||
#If specified outputs the data to the pipeline (you can add -whatif so it the command only outputs to the command)
|
||||
[switch]$Passthru ,
|
||||
#If specified, opens the output workbook.
|
||||
[Switch]$Show
|
||||
)
|
||||
|
||||
#region Read Excel data
|
||||
if ($Referencefile -and $Differencefile) {
|
||||
#if the filenames don't resolve, give up now.
|
||||
try { $oneFile = ((Resolve-Path -Path $Referencefile -ErrorAction Stop).path -eq (Resolve-Path -Path $Differencefile -ErrorAction Stop).path)}
|
||||
Catch { Write-Warning -Message "Could not Resolve the filenames." ; return }
|
||||
|
||||
#If we have one file , we must have two different worksheet names. If we have two files $worksheetName can be a single string or two strings.
|
||||
if ($onefile -and ( ($WorkSheetName.count -ne 2) -or $WorkSheetName[0] -eq $WorkSheetName[1] ) ) {
|
||||
Write-Warning -Message "If both the Reference and difference file are the same then worksheet name must provide 2 different names"
|
||||
return
|
||||
}
|
||||
if ($WorkSheetName.count -eq 2) {$workSheet2 = $DiffPrefix = $WorkSheetName[1] ; $worksheet1 = $WorkSheetName[0] ; }
|
||||
elseif ($WorkSheetName -is [string]) {$worksheet2 = $workSheet1 = $WorkSheetName ;
|
||||
$DiffPrefix = (Split-Path -Path $Differencefile -Leaf) -replace "\.xlsx$","" }
|
||||
else {Write-Warning -Message "You must provide either a single worksheet name or two names." ; return }
|
||||
|
||||
$params= @{ ErrorAction = [System.Management.Automation.ActionPreference]::Stop }
|
||||
foreach ($p in @("HeaderName","NoHeader","StartRow")) {if ($PSBoundParameters[$p]) {$params[$p] = $PSBoundParameters[$p]}}
|
||||
try {
|
||||
$ReferenceObject = Import-Excel -Path $Referencefile -WorksheetName $WorkSheet1 @params
|
||||
$DifferenceObject = Import-Excel -Path $Differencefile -WorksheetName $WorkSheet2 @Params
|
||||
}
|
||||
Catch {Write-Warning -Message "Could not read the worksheet from $Referencefile::$worksheet1 and/or $Differencefile::$worksheet2." ; return }
|
||||
if ($NoHeader) {$firstDataRow = $Startrow } else {$firstDataRow = $Startrow + 1}
|
||||
}
|
||||
elseif ( $Differencefile) {
|
||||
if ($WorkSheetName -isnot [string]) {Write-Warning -Message "You must provide a single worksheet name." ; return }
|
||||
$params = @{WorkSheetName=$WorkSheetName; Path=$Differencefile; ErrorAction = [System.Management.Automation.ActionPreference]::Stop ;}
|
||||
try {$DifferenceObject = Import-Excel @Params }
|
||||
Catch {Write-Warning -Message "Could not read the worksheet '$WorkSheetName' from $Differencefile::$WorkSheetName." ; return }
|
||||
$DiffPrefix = (Split-Path -Path $Differencefile -Leaf) -replace "\.xlsx$",""
|
||||
if ($NoHeader) {$firstDataRow = $Startrow } else {$firstDataRow = $Startrow + 1}
|
||||
}
|
||||
else { $firstDataRow = 1 }
|
||||
#endregion
|
||||
|
||||
#region Set lists of properties and row numbers
|
||||
#Make a list of properties/headings using the Property (default "*") and ExcludeProperty parameters
|
||||
$propList = @()
|
||||
$DifferenceObject = $DifferenceObject | Update-FirstObjectProperties
|
||||
$headings = $DifferenceObject[0].psobject.Properties.Name # This preserves the sequence - using get-member would sort them alphabetically! There may be extra properties in
|
||||
if ($NoHeader -and "Name" -eq $Key) {$Key = "p1"}
|
||||
if ($headings -notcontains $Key -and
|
||||
('*' -ne $Key)) {Write-Warning -Message "You need to specify one of the headings in the sheet '$worksheet1' as a key." ; return }
|
||||
foreach ($p in $Property) { $propList += ($headings.where({$_ -like $p}) )}
|
||||
foreach ($p in $ExcludeProperty) { $propList = $propList.where({$_ -notlike $p}) }
|
||||
if (($propList -notcontains $Key) -and
|
||||
('*' -ne $Key)) { $propList += $Key} #If $key isn't one of the headings we will have bailed by now
|
||||
$propList = $propList | Select-Object -Unique #so, prolist must contain at least $key if nothing else
|
||||
|
||||
#If key is "*" we treat it differently , and we will create a script property which concatenates all the Properties in $Proplist
|
||||
$ConCatblock = [scriptblock]::Create( ($proplist | ForEach-Object {'$this."' + $_ + '"'}) -join " + ")
|
||||
|
||||
#Build the list of the properties to output, in order.
|
||||
$diffpart = @()
|
||||
$refpart = @()
|
||||
foreach ($p in $proplist.Where({$key -ne $_}) ) {$refPart += $p ; $diffPart += "$DiffPrefix $p" }
|
||||
#Last reference column will be A if there the only one property (which might be the key), B if there are two properties, C if there are 3 etc
|
||||
$lastRefCol = [char](64 + $propList.count)
|
||||
#First difference column will be the next one (we'll trap the case of only having the key later)
|
||||
$FirstDiffCol = [char](65 + $propList.count)
|
||||
|
||||
if ($key -ne '*') {
|
||||
$outputProps = @($key) + $refpart + $diffpart
|
||||
#If we are using a single column as the key, don't duplicate it, so the last difference column will be A if there is one property, C if there are two, E if there are 3
|
||||
$lastDiffCol = [char](63 + 2 * $propList.count)
|
||||
}
|
||||
else {
|
||||
$outputProps = @( ) + $refpart + $diffpart
|
||||
#If we not using a single column as a key all columns are duplicated so, the Last difference column will be B if there is one property, D if there are two, F if there are 3
|
||||
$lastDiffCol = [char](64 + 2 * $propList.count)
|
||||
}
|
||||
|
||||
#Add RowNumber to every row
|
||||
#If one sheet has extra rows we can get a single "==" result from compare, with the row from the reference sheet, but
|
||||
#the row in the other sheet might be different so we will look up the row number from the key field - build a hash table for that here
|
||||
#If we have "*" as the key ad the script property to concatenate the [selected] properties.
|
||||
|
||||
$Rowhash = @{}
|
||||
$rowNo = $firstDataRow
|
||||
foreach ($row in $ReferenceObject) {
|
||||
if ($row._row -eq $null) {Add-Member -InputObject $row -MemberType NoteProperty -Value ($rowNo ++) -Name "_Row" }
|
||||
else {$rowNo++ }
|
||||
if ($Key -eq '*' ) {Add-Member -InputObject $row -MemberType ScriptProperty -Value $ConCatblock -Name "_All" }
|
||||
}
|
||||
$rowNo = $firstDataRow
|
||||
foreach ($row in $DifferenceObject) {
|
||||
Add-Member -InputObject $row -MemberType NoteProperty -Value $rowNo -Name "$DiffPrefix Row" -Force
|
||||
if ($Key -eq '*' ) {
|
||||
Add-Member -InputObject $row -MemberType ScriptProperty -Value $ConCatblock -Name "_All"
|
||||
$Rowhash[$row._All] = $rowNo
|
||||
}
|
||||
else {$Rowhash[$row.$key] = $rowNo }
|
||||
$rowNo ++
|
||||
}
|
||||
if ($Key -eq '*') {$key = "_ALL"}
|
||||
#endregion
|
||||
$expandedDiff = Compare-Object -ReferenceObject $ReferenceObject -DifferenceObject $DifferenceObject -Property $propList -PassThru -IncludeEqual |
|
||||
Group-Object -Property $key | ForEach-Object {
|
||||
#The value of the key column is the name of the group.
|
||||
$keyval = $_.name
|
||||
#we're going to create a custom object from a hash table. ??Might no longer need to preserve the field order
|
||||
$hash = [ordered]@{}
|
||||
foreach ($result in $_.Group) {
|
||||
if ($result.SideIndicator -ne "=>") {$hash["_Row"] = $result._Row }
|
||||
elseif (-not $hash["$DiffPrefix Row"]) {$hash["_Row"] = "" }
|
||||
#if we have already set the side, be this must the second record, so set side to indicate "changed"
|
||||
if ($hash.Side) {$hash.Side = "<>"} else {$hash["Side"] = $result.SideIndicator}
|
||||
switch ($hash.side) {
|
||||
'==' { $hash["$DiffPrefix is"] = 'Same' }
|
||||
'=>' { $hash["$DiffPrefix is"] = 'Added' }
|
||||
'<>' { if (-not $hash["_Row"]) {
|
||||
$hash["$DiffPrefix is"] = 'Added'
|
||||
}
|
||||
else {
|
||||
$hash["$DiffPrefix is"] = 'Changed'
|
||||
}
|
||||
}
|
||||
'<=' { $hash["$DiffPrefix is"] = 'Removed'}
|
||||
}
|
||||
#find the number of the row in the the "difference" object which has this key. If it is the object is only the reference this will be blank.
|
||||
$hash["$DiffPrefix Row"] = $Rowhash[$keyval]
|
||||
$hash[$key] = $keyval
|
||||
#Create FieldName and/or =>FieldName columns
|
||||
foreach ($p in $result.psobject.Properties.name.where({$_ -ne $key -and $_ -ne "SideIndicator" -and $_ -ne "$DiffPrefix Row" })) {
|
||||
if ($result.SideIndicator -eq "==" -and $p -in $propList)
|
||||
{$hash[("$p")] = $hash[("$DiffPrefix $p")] = $result.$P}
|
||||
elseif ($result.SideIndicator -eq "==" -or $result.SideIndicator -eq "<=")
|
||||
{$hash[("$p")] = $result.$P}
|
||||
elseif ($result.SideIndicator -eq "=>") { $hash[("$DiffPrefix $p")] = $result.$P}
|
||||
}
|
||||
}
|
||||
[Pscustomobject]$hash
|
||||
} | Sort-Object -Property "_row"
|
||||
|
||||
#Already sorted by reference row number, fill in any blanks in the difference-row column
|
||||
for ($i = 1; $i -lt $expandedDiff.Count; $i++) {if (-not $expandedDiff[$i]."$DiffPrefix Row") {$expandedDiff[$i]."$DiffPrefix Row" = $expandedDiff[$i-1]."$DiffPrefix Row" } }
|
||||
|
||||
#Now re-Sort by difference row number, and fill in any blanks in the reference-row column
|
||||
$expandedDiff = $expandedDiff | Sort-Object -Property "$DiffPrefix Row"
|
||||
for ($i = 1; $i -lt $expandedDiff.Count; $i++) {if (-not $expandedDiff[$i]."_Row") {$expandedDiff[$i]."_Row" = $expandedDiff[$i-1]."_Row" } }
|
||||
|
||||
$AllProps = @("_Row") + $OutputProps + $expandedDiff[0].psobject.properties.name.where({$_ -notin ($outputProps + @("_row","side","SideIndicator","_ALL" ))})
|
||||
|
||||
if ($PassThru -or -not $OutputFile) {return ($expandedDiff | Select-Object -Property $allprops | Sort-Object -Property "_row", "$DiffPrefix Row" | Update-FirstObjectProperties ) }
|
||||
elseif ($PSCmdlet.ShouldProcess($OutputFile,"Write Output to Excel file")) {
|
||||
$expandedDiff = $expandedDiff | Sort-Object -Property "_row", "$DiffPrefix Row"
|
||||
$xl = $expandedDiff | Select-Object -Property $OutputProps | Update-FirstObjectProperties |
|
||||
Export-Excel -Path $OutputFile -WorkSheetname $OutputSheetName -FreezeTopRow -BoldTopRow -AutoSize -AutoFilter -PassThru
|
||||
$ws = $xl.Workbook.Worksheets[$OutputSheetName]
|
||||
for ($i = 0; $i -lt $expandedDiff.Count; $i++ ) {
|
||||
if ( $expandedDiff[$i].side -ne "==" ) {
|
||||
Set-Format -WorkSheet $ws -Range ("A" + ($i + 2 )) -FontColor $KeyFontColor
|
||||
}
|
||||
elseif ( $HideEqual ) {$ws.row($i+2).hidden = $true }
|
||||
if ( $expandedDiff[$i].side -eq "<>" ) {
|
||||
$range = $ws.Dimension -replace "\d+", ($i + 2 )
|
||||
Set-Format -WorkSheet $ws -Range $range -BackgroundColor $ChangeBackgroundColor
|
||||
}
|
||||
elseif ( $expandedDiff[$i].side -eq "<=" ) {
|
||||
$range = "A" + ($i + 2 ) + ":" + $lastRefCol + ($i + 2 )
|
||||
Set-Format -WorkSheet $ws -Range $range -BackgroundColor $DeleteBackgroundColor
|
||||
}
|
||||
elseif ( $expandedDiff[$i].side -eq "=>" ) {
|
||||
if ($propList.count -gt 1) {
|
||||
$range = $FirstDiffCol + ($i + 2 ) + ":" + $lastDiffCol + ($i + 2 )
|
||||
Set-Format -WorkSheet $ws -Range $range -BackgroundColor $AddBackgroundColor
|
||||
}
|
||||
Set-Format -WorkSheet $ws -Range ("A" + ($i + 2 )) -BackgroundColor $AddBackgroundColor
|
||||
}
|
||||
}
|
||||
Close-ExcelPackage -ExcelPackage $xl -Show:$Show
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,9 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi
|
||||
|
||||
# What's new
|
||||
|
||||
- [James O'Neill](https://twitter.com/jamesoneill) added `Compare-Worksheet`
|
||||
- Compares two worksheets with the same name in different files.
|
||||
|
||||
#### 4/22/2018
|
||||
Thanks to the community yet again
|
||||
- [ili101](https://github.com/ili101) for fixes and features
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
Function Send-SQLDataToExcel {
|
||||
<#
|
||||
[CmdLetBinding()]
|
||||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")]
|
||||
<#
|
||||
.Synopsis
|
||||
Runs a SQL query and inserts the results into an ExcelSheet, more efficiently than sending it via Export-Excel
|
||||
.Description
|
||||
@@ -23,7 +25,7 @@
|
||||
|
||||
|
||||
|
||||
#>
|
||||
#>
|
||||
param (
|
||||
#Database connection string; either DSN=ODBC_Data_Source_Name, a full odbc or SQL Connection string, or the name of a SQL server
|
||||
[Parameter(ParameterSetName="SQLConnection", Mandatory=$true)]
|
||||
@@ -41,6 +43,9 @@
|
||||
#The SQL query to run
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$SQL,
|
||||
#Override the default query time of 30 seconds.
|
||||
[int]$QueryTimeout,
|
||||
#File name for the Excel File
|
||||
$Path,
|
||||
[String]$WorkSheetname = 'Sheet1',
|
||||
[Switch]$KillExcel,
|
||||
@@ -92,9 +97,9 @@
|
||||
#We were either given a session object or a connection string (with, optionally a MSSQLServer parameter)
|
||||
# If we got -MSSQLServer, create a SQL connection, if we didn't but we got -Connection create an ODBC connection
|
||||
if ($MsSQLserver) {
|
||||
if ($connection -notmatch "=") {$Connection = "server=$Connection;trusted_connection=true;timeout=60"}
|
||||
if ($Connection -notmatch "=") {$Connection = "server=$Connection;trusted_connection=true;timeout=60"}
|
||||
$Session = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $Connection
|
||||
if ($Session.State -ne 'Open') {$session.Open()}
|
||||
if ($Session.State -ne 'Open') {$Session.Open()}
|
||||
if ($DataBase) {$Session.ChangeDatabase($DataBase) }
|
||||
}
|
||||
elseif ($Connection) {
|
||||
@@ -102,30 +107,31 @@
|
||||
}
|
||||
|
||||
#A session was either passed in or just created. If it's a SQL one make a SQL DataAdapter, otherwise make an ODBC one
|
||||
if ($Session.gettype().name -match "SqlConnection") {
|
||||
if ($Session.GetType().name -match "SqlConnection") {
|
||||
$dataAdapter = New-Object -TypeName System.Data.SqlClient.SqlDataAdapter -ArgumentList (
|
||||
New-Object -TypeName System.Data.SqlClient.SqlCommand -ArgumentList $sql, $Session)
|
||||
New-Object -TypeName System.Data.SqlClient.SqlCommand -ArgumentList $SQL, $Session)
|
||||
}
|
||||
else {
|
||||
$dataAdapter = New-Object -TypeName System.Data.Odbc.OdbcDataAdapter -ArgumentList (
|
||||
New-Object -TypeName System.Data.Odbc.OdbcCommand -ArgumentList $sql, $Session )
|
||||
New-Object -TypeName System.Data.Odbc.OdbcCommand -ArgumentList $SQL, $Session )
|
||||
}
|
||||
|
||||
if ($QueryTimeout) {$dataAdapter.SelectCommand.CommandTimeout = $ServerTimeout}
|
||||
|
||||
#Both adapter types output the same kind of table, create one and fill it from the adapter
|
||||
$dataTable = New-Object -TypeName System.Data.DataTable
|
||||
$rowCount = $dataAdapter.fill($dataTable)
|
||||
Write-Verbose "Query returned $rowcount row(s)"
|
||||
Write-Verbose -Message "Query returned $rowCount row(s)"
|
||||
|
||||
#ExportExcel user a -NoHeader parameter so that's what we use here, but needs to be the other way around.
|
||||
$PrintHeaders = -not $NoHeader
|
||||
$printHeaders = -not $NoHeader
|
||||
if ($Title) {$r = $StartRow +1 }
|
||||
else {$r = $StartRow}
|
||||
#Get our Excel sheet and fill it with the data
|
||||
$excelPackage = Export-Excel -Path $Path -WorkSheetname $WorkSheetname -PassThru
|
||||
$excelPackage.Workbook.Worksheets[$WorkSheetname].Cells[$r,$StartColumn].LoadFromDataTable($dataTable, $PrintHeaders ) | Out-Null
|
||||
$excelPackage.Workbook.Worksheets[$WorkSheetname].Cells[$r,$StartColumn].LoadFromDataTable($dataTable, $printHeaders ) | Out-Null
|
||||
|
||||
#Call export-excel with any parameters which don't relate to the SQL query
|
||||
"Connection", "Database" , "Session", "MsSQLserver", "Destination" , "sql" ,"Path" | foreach-object {$null = $PSBoundParameters.Remove($_) }
|
||||
"Connection", "Database" , "Session", "MsSQLserver", "Destination" , "SQL" ,"Path" | ForEach-Object {$null = $PSBoundParameters.Remove($_) }
|
||||
Export-Excel -ExcelPackage $excelPackage @PSBoundParameters
|
||||
|
||||
#If we were not passed a session close the session we created.
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
This version of the previous command lightlights all the cells in lightgray and then sets the changed rows back to white; only
|
||||
the unchanged rows are highlighted
|
||||
#>
|
||||
[cmdletbinding(DefaultParameterSetName)]
|
||||
[cmdletbinding(DefaultParameterSetName)]
|
||||
Param(
|
||||
#First file to compare
|
||||
[parameter(Mandatory=$true,Position=0)]
|
||||
@@ -135,7 +135,7 @@
|
||||
|
||||
if ($ExcludeDifferent -and -not $IncludeEqual) {$IncludeEqual = $true}
|
||||
#Do the comparison and add file,sheet and row to the result - these are prefixed with "_" to show they are added the addition will fail if the sheet has these properties so split the operations
|
||||
$diff = Compare-Object -ReferenceObject $Sheet1 -DifferenceObject $Sheet2 -Property $propList -PassThru -IncludeEqual:$IncludeEqual -ExcludeDifferent:$ExcludeDifferent |
|
||||
[PSCustomObject[]]$diff = Compare-Object -ReferenceObject $Sheet1 -DifferenceObject $Sheet2 -Property $propList -PassThru -IncludeEqual:$IncludeEqual -ExcludeDifferent:$ExcludeDifferent |
|
||||
Sort-Object -Property "_Row","File"
|
||||
|
||||
#if BackgroundColor was specified, set it on extra or extra or changed rows
|
||||
@@ -168,7 +168,7 @@
|
||||
}
|
||||
#if font colour was specified, set it on changed properties where the same key appears in both sheets.
|
||||
if ($diff -and $FontColor -and ($propList -contains $Key) ) {
|
||||
$updates = $diff.where({$_.SideIndicator -ne "=="}) | Group-object -Property $Key | where {$_.count -eq 2}
|
||||
$updates = $diff.where({$_.SideIndicator -ne "=="}) | Group-object -Property $Key | Where-Object {$_.count -eq 2}
|
||||
if ($updates) {
|
||||
$XL1 = Open-ExcelPackage -path $Referencefile
|
||||
if ($oneFile ) {$xl2 = $xl1}
|
||||
|
||||
Reference in New Issue
Block a user