mirror of
https://github.com/dfinke/ImportExcel.git
synced 2025-12-11 05:43:17 +00:00
Added join Worksheet
Moved "MoveTo…" functionality into Add-Worksheet and gave it copy worksheet functionality Added create new file functionality to Open-ExcelPackage - requires a -create switch so previous "Open for read if it exists" behaviour is kept. Fixed Conditional formatting so background pattern is "None" not "Solid" by default. Tidied comments and help in Merge and Compare Added Join Worksheet Added Extra parameters, sanity check and help to New-PivotTableDefinition
This commit is contained in:
@@ -1,20 +1,20 @@
|
||||
Function Add-ConditionalFormatting {
|
||||
<#
|
||||
.Synopsis
|
||||
Adds contitional formatting to worksheet
|
||||
Adds contitional formatting to worksheet.
|
||||
.Example
|
||||
$excel = $avdata | Export-Excel -Path (Join-path $FilePath "\Machines.XLSX" ) -WorksheetName "Server Anti-Virus" -AutoSize -FreezeTopRow -AutoFilter -PassThru
|
||||
|
||||
Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "b":b1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "2003"
|
||||
Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "b2:b1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "2003"
|
||||
Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "i2:i1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "Disabled"
|
||||
$excel.Workbook.Worksheets[1].Cells["D1:G1048576"].Style.Numberformat.Format = [cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern
|
||||
$excel.Workbook.Worksheets[1].Row(1).style.font.bold = $true
|
||||
$excel.Save() ; $excel.Dispose()
|
||||
|
||||
Here Export-Excel is called with the -passThru parameter so the Excel Package object is stored in $Excel
|
||||
The desired worksheet is selected and the then columns B and i are conditially formatted (excluding the top row) to show
|
||||
Fixed formats are then applied to dates in columns D..G and the top row is formatted
|
||||
Finally the workbook is saved and the Excel closed.
|
||||
The desired worksheet is selected and the then columns B and i are conditially formatted (excluding the top row) to show red text if
|
||||
the columns contain "2003" or "Disabled respectively. A fixed date formats are then applied to columns D..G, and the top row is formatted.
|
||||
Finally the workbook is saved and the Excel object closed.
|
||||
|
||||
#>
|
||||
Param (
|
||||
@@ -70,7 +70,7 @@
|
||||
#Background colour for matching items
|
||||
[System.Drawing.Color]$BackgroundColor,
|
||||
#Background pattern for matching items
|
||||
[OfficeOpenXml.Style.ExcelFillStyle]$BackgroundPattern = [OfficeOpenXml.Style.ExcelFillStyle]::Solid,
|
||||
[OfficeOpenXml.Style.ExcelFillStyle]$BackgroundPattern = [OfficeOpenXml.Style.ExcelFillStyle]::None ,
|
||||
#Secondary colour when a background pattern requires it
|
||||
[System.Drawing.Color]$PatternColor,
|
||||
#Sets the numeric format for matching items
|
||||
@@ -84,13 +84,12 @@
|
||||
#Strikethrough text of matching items
|
||||
[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) {
|
||||
#Allow 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) {
|
||||
$WorkSheet = $Address.Worksheet[0]
|
||||
$Range = $Address.Address
|
||||
}
|
||||
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)}
|
||||
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) }
|
||||
|
||||
251
Compare-WorkSheet.ps1
Normal file
251
Compare-WorkSheet.ps1
Normal file
@@ -0,0 +1,251 @@
|
||||
Function Compare-Worksheet {
|
||||
<#
|
||||
.Synopsis
|
||||
Compares two worksheets (usually with the same name in different files).
|
||||
.Description
|
||||
This command takes two file names, one or two worksheet name(s) and a name for a 'key' column.
|
||||
It reads the worksheets and determines which column will be compared and builds a hashtable of the values in the "key column" values and the rows they appear in.
|
||||
It then uses PowerShell's Compare-Object command to compare the sheets (explicity checking all the column names it selected).
|
||||
For the 'difference' rows it adds the row number for the key of that row - we have to add the key *after* doing the comparison,
|
||||
otherwise rows will be considered as different simply because they have different row numbers.
|
||||
We also add the name of the file in which the difference occurs to the result.
|
||||
If -BackgroundColor is specified the difference rows in the source spreadsheet will have their background changed to identify the different rows.
|
||||
.Example
|
||||
Compare-WorkSheet -Referencefile 'Server56.xlsx' -Differencefile 'Server57.xlsx' -WorkSheetName Products -key IdentifyingNumber -ExcludeProperty Install* | format-table
|
||||
The two workbooks in this example contain the result of redirecting a subset of properties from Get-WmiObject -Class win32_product to Export-Excel.
|
||||
The command compares the "products" pages in the two workbooks, but we don't want to register a differnce if if the software was installed on a
|
||||
different date or from a different place, so specify -ExcludeProperty Install* removes InstallDate and InstallSource from the comparison.
|
||||
This data doesn't have a "name" column" so we specify the "IdentifyingNumber" column to be the key.
|
||||
PowerShell will output the differences formatted as a table.
|
||||
.Example
|
||||
Compare-WorkSheet "Server54.xlsx" "Server55.xlsx" -WorkSheetName services -GridView
|
||||
This time two workbooks contain the result of redirecting Get-WmiObject -Class win32_service to Export-Excel.
|
||||
Here the -Differencefile and -Referencefile parameter switches are assumed , and the default setting for -key ("Name") works for services.
|
||||
This will display the differences between the "services" sheets using a grid view
|
||||
.Example
|
||||
Compare-WorkSheet 'Server54.xlsx' 'Server55.xlsx' -WorkSheetName Services -BackgroundColor lightGreen
|
||||
This version of the command outputs the differences between the "services" pages and also highlights any different rows in the spreadsheet files.
|
||||
.Example
|
||||
Compare-WorkSheet 'Server54.xlsx' 'Server55.xlsx' -WorkSheetName Services -BackgroundColor lightGreen -FontColor Red -Show
|
||||
This builds on the previous example: this time where two changed rows have the value in the "name" column (the default value for -key),
|
||||
the command adds highlighting of the changed cells in red; and then opens the Excel file.
|
||||
.Example
|
||||
Compare-WorkSheet 'Pester-tests.xlsx' 'Pester-tests.xlsx' -WorkSheetName 'Server1','Server2' -Property "full Description","Executed","Result" -Key "full Description"
|
||||
This time the reference file and the difference file are the same file and two different sheets are used. Because the tests include the
|
||||
machine name and time the test was run the command specifies a limited set of columns should be compared.
|
||||
.Example
|
||||
Compare-WorkSheet 'Server54.xlsx' 'Server55.xlsx' -WorkSheetName general -Startrow 2 -Headername Label,value -Key Label -GridView -ExcludeDifferent
|
||||
The "General" page has a title and two unlabelled columns with a row for CPU, Memory, Domain, Disk and so on.
|
||||
So the command is instructed to start at row 2 (skipping the title) and to name the columns: the first is "label" and the second "Value" with label acting as the key.
|
||||
This time we are interested only in the rows which are the same in both sheets, and the result is displayed using grid view.
|
||||
Note that grid view works best when the number of columns is small.
|
||||
.Example
|
||||
Compare-WorkSheet 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName general -Startrow 2 -Headername Label,value -Key Label -BackgroundColor White -Show -AllDataBackgroundColor LightGray
|
||||
This version of the previous command highlights all the cells in lightgray and then sets the changed rows back to white so that
|
||||
only the unchanged rows are highlighted
|
||||
#>
|
||||
[cmdletbinding(DefaultParameterSetName)]
|
||||
Param(
|
||||
#First file to compare.
|
||||
[parameter(Mandatory=$true,Position=0)]
|
||||
$Referencefile ,
|
||||
#Second file to compare.
|
||||
[parameter(Mandatory=$true,Position=1)]
|
||||
$Differencefile ,
|
||||
#Name(s) of worksheets to compare.
|
||||
$WorkSheetName = "Sheet1",
|
||||
#Properties to include in the DIFF - supports wildcards, default is "*".
|
||||
$Property = "*" ,
|
||||
#Properties to exclude from the the search - supports wildcards.
|
||||
$ExcludeProperty ,
|
||||
#Specifies custom property names to use, instead of the values defined in the column headers of the Start row .
|
||||
[Parameter(ParameterSetName='B', Mandatory)]
|
||||
[String[]]$Headername,
|
||||
#Automatically generate property names (P1, P2, P3, ..) instead of the using the values the Start row of the sheet.
|
||||
[Parameter(ParameterSetName='C', Mandatory)]
|
||||
[switch]$NoHeader,
|
||||
#The row from where we start to import data, all rows above the Start row are disregarded. By default this is row 1.
|
||||
[int]$Startrow = 1,
|
||||
#If specified, highlights all the cells - so you can make Equal cells one colour, and Diff cells another.
|
||||
[System.Drawing.Color]$AllDataBackgroundColor,
|
||||
#If specified, highlights the DIFF rows.
|
||||
[System.Drawing.Color]$BackgroundColor,
|
||||
#If specified identifies the tabs which contain DIFF rows (ignored if -backgroundColor is omitted).
|
||||
[System.Drawing.Color]$TabColor,
|
||||
#Name of a column which is unique and will be used to add a row to the DIFF object, default is "Name" .
|
||||
$Key = "Name" ,
|
||||
#If specified, highlights the DIFF columns in rows which have the same key.
|
||||
[System.Drawing.Color]$FontColor,
|
||||
#If specified opens the Excel workbooks instead of outputting the diff to the console (unless -Passthru is also specified) .
|
||||
[Switch]$Show,
|
||||
#If specified, the command tries to the show the DIFF in a Gridview and not on the console. (unless -Passthru is also specified). This Works best with few columns selected, and requires a key .
|
||||
[switch]$GridView,
|
||||
#If specified -Passthrough full set of diff data is returned without filtering to the specified properties
|
||||
[Switch]$PassThru,
|
||||
#If specified the result will include equal rows as well. By default only different rows are returned.
|
||||
[Switch]$IncludeEqual,
|
||||
#If Specified the result includes only the rows where both are equal.
|
||||
[Switch]$ExcludeDifferent
|
||||
)
|
||||
|
||||
#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 mush have two different worksheet names. If we have two files we can 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) {$worksheet1 = $WorkSheetName[0] ; $WorkSheet2 = $WorkSheetName[1]}
|
||||
elseif ($WorkSheetName -is [string]) {$worksheet1 = $WorkSheet2 = $WorkSheetName}
|
||||
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 {
|
||||
$Sheet1 = Import-Excel -Path $Referencefile -WorksheetName $WorkSheet1 @params
|
||||
$Sheet2 = Import-Excel -Path $Differencefile -WorksheetName $WorkSheet2 @Params
|
||||
}
|
||||
Catch {Write-Warning -Message "Could not read the worksheet from $Referencefile and/or $Differencefile." ; return }
|
||||
|
||||
#Get Column headings and create a hash table of Name to column letter.
|
||||
$headings = $Sheet1[-1].psobject.Properties.name # This preserves the sequence - using get-member would sort them alphabetically!
|
||||
$headings | ForEach-Object -Begin {$columns = @{} ; $i=65 } -Process {$Columns[$_] = [char]($i ++) }
|
||||
|
||||
#Make a list of property headings using the Property (default "*") and ExcludeProperty parameters.
|
||||
if ($Key -eq "Name" -and $NoHeader) {$key = "p1"}
|
||||
$propList = @()
|
||||
foreach ($p in $Property) {$propList += ($headings.where({$_ -like $p}) )}
|
||||
foreach ($p in $ExcludeProperty) {$propList = $propList.where({$_ -notlike $p}) }
|
||||
if (($headings -contains $key) -and ($propList -notcontains $Key)) {$propList += $Key}
|
||||
$propList = $propList | Select-Object -Unique
|
||||
if ($propList.Count -eq 0) {Write-Warning -Message "No Columns are selected with -Property = '$Property' and -excludeProperty = '$ExcludeProperty'." ; return}
|
||||
|
||||
#Add RowNumber, Sheetname and file name to every row.
|
||||
$FirstDataRow = $startRow + 1
|
||||
if ($Headername -or $NoHeader) {$FirstDataRow -- }
|
||||
$i = $FirstDataRow ; foreach ($row in $Sheet1) {Add-Member -InputObject $row -MemberType NoteProperty -Name "_Row" -Value ($i ++)
|
||||
Add-Member -InputObject $row -MemberType NoteProperty -Name "_Sheet" -Value $worksheet1
|
||||
Add-Member -InputObject $row -MemberType NoteProperty -Name "_File" -Value $Referencefile}
|
||||
$i = $FirstDataRow ; foreach ($row in $Sheet2) {Add-Member -InputObject $row -MemberType NoteProperty -Name "_Row" -Value ($i ++)
|
||||
Add-Member -InputObject $row -MemberType NoteProperty -Name "_Sheet" -Value $worksheet2
|
||||
Add-Member -InputObject $row -MemberType NoteProperty -Name "_File" -Value $Differencefile}
|
||||
|
||||
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 .
|
||||
[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.
|
||||
if ($diff -and $BackgroundColor) {
|
||||
#Differences may only exist in one file. So gather the changes for each file; open the file, update each impacted row in the shee, save the file .
|
||||
$updates = $diff.where({$_.SideIndicator -ne "=="}) | Group-object -Property "_File"
|
||||
foreach ($file in $updates) {
|
||||
try {$xl = Open-ExcelPackage -Path $file.name }
|
||||
catch {Write-warning -Message "Can't open $($file.Name) for writing." ; return}
|
||||
if ($AllDataBackgroundColor) {
|
||||
$file.Group._sheet | Sort-Object -Unique | ForEach-Object {
|
||||
$ws = $xl.Workbook.Worksheets[$_]
|
||||
if ($headerName) {$range = "A" + $startrow + ":" + $ws.dimension.end.address}
|
||||
else {$range = "A" + ($startrow + 1) + ":" + $ws.dimension.end.address}
|
||||
Set-Format -WorkSheet $ws -BackgroundColor $AllDataBackgroundColor -Range $Range
|
||||
}
|
||||
}
|
||||
foreach ($row in $file.group) {
|
||||
$ws = $xl.Workbook.Worksheets[$row._Sheet]
|
||||
$range = $ws.Dimension -replace "\d+",$row._row
|
||||
Set-Format -WorkSheet $ws -Range $range -BackgroundColor $BackgroundColor
|
||||
}
|
||||
if ($TabColor) {
|
||||
foreach ($tab in ($file.group._sheet | Select-Object -Unique)) {
|
||||
$xl.Workbook.Worksheets[$tab].TabColor = $TabColor
|
||||
}
|
||||
}
|
||||
$xl.save() ; $xl.Stream.Close() ; $xl.Dispose()
|
||||
}
|
||||
}
|
||||
#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-Object {$_.count -eq 2}
|
||||
if ($updates) {
|
||||
$XL1 = Open-ExcelPackage -path $Referencefile
|
||||
if ($oneFile ) {$xl2 = $xl1}
|
||||
else {$xl2 = Open-ExcelPackage -path $Differencefile }
|
||||
foreach ($u in $updates) {
|
||||
foreach ($p in $propList) {
|
||||
if($u.Group[0].$p -ne $u.Group[1].$p ) {
|
||||
Set-Format -WorkSheet $xl1.Workbook.Worksheets[$u.Group[0]._sheet] -Range ($Columns[$p] + $u.Group[0]._Row) -FontColor $FontColor
|
||||
Set-Format -WorkSheet $xl2.Workbook.Worksheets[$u.Group[1]._sheet] -Range ($Columns[$p] + $u.Group[1]._Row) -FontColor $FontColor
|
||||
}
|
||||
}
|
||||
}
|
||||
$xl1.Save() ; $xl1.Stream.Close() ; $xl1.Dispose()
|
||||
if (-not $oneFile) {$xl2.Save() ; $xl2.Stream.Close() ; $xl2.Dispose()}
|
||||
}
|
||||
}
|
||||
elseif ($diff -and $FontColor) {Write-Warning -Message "To match rows to set changed cells, you must specify -Key and it must match one of the included properties." }
|
||||
|
||||
#if nothing was found write a message which wont be redirected.
|
||||
if (-not $diff) {Write-Host "Comparison of $Referencefile::$worksheet1 and $Differencefile::$WorkSheet2 returned no results." }
|
||||
|
||||
if ($show) {
|
||||
Start-Process -FilePath $Referencefile
|
||||
if (-not $oneFile) { Start-Process -FilePath $Differencefile }
|
||||
if ($GridView) { Write-Warning -Message "-GridView is ignored when -Show is specified" }
|
||||
}
|
||||
elseif ($GridView -and $propList -contains $key) {
|
||||
|
||||
|
||||
if ($IncludeEqual -and -not $ExcludeDifferent) {
|
||||
$GroupedRows = $diff | Group-Object -Property $key
|
||||
}
|
||||
else { #to get the right now numbers on the grid we need to have all the rows.
|
||||
$GroupedRows = Compare-Object -ReferenceObject $Sheet1 -DifferenceObject $Sheet2 -Property $propList -PassThru -IncludeEqual |
|
||||
Group-Object -Property $key
|
||||
}
|
||||
#Additions, deletions and unchanged rows will give a group of 1; changes will give a group of 2 .
|
||||
|
||||
#If one sheet has extra rows we can get a single "==" result from compare, but with the row from the reference sheet
|
||||
#but the row in the other sheet might so we will look up the row number from the key field build a hash table for that
|
||||
$Sheet2 | ForEach-Object -Begin {$Rowhash = @{} } -Process {$Rowhash[$_.$key] = $_._row }
|
||||
|
||||
$ExpandedDiff = ForEach ($g in $GroupedRows) {
|
||||
#we're going to create a custom object from a hash table. We want the fields to be ordered.
|
||||
$hash = [ordered]@{}
|
||||
foreach ($result IN $g.Group) {
|
||||
# if result indicates equal or "in Reference" set the reference side row. If we did that on a previous result keep it. Otherwise set to "blank".
|
||||
if ($result.sideindicator -ne "=>") {$hash["<Row"] = $result._Row }
|
||||
elseif (-not $hash["<Row"]) {$hash["<Row"] = "" }
|
||||
#if we have already set the side, this is the second record, so set side to indicate "changed"
|
||||
if ($hash.Side) {$hash.side = "<>"} else {$hash["Side"] = $result.sideindicator}
|
||||
#if result is "in reference" and we don't have a matching "in difference" (meaning a change) the lookup will be blank. Which we want.
|
||||
$hash[">Row"] = $Rowhash[$g.Name]
|
||||
#position the key as the next field (only appears once)
|
||||
$Hash[$key] = $g.Name
|
||||
#For all the other fields we care about, create <=FieldName and/or =>FieldName .
|
||||
foreach ($p in $propList.Where({$_ -ne $key})) {
|
||||
if ($result.SideIndicator -eq "==") {$hash[("=>$P")] = $hash[("<=$P")] =$result.$P}
|
||||
else {$hash[($result.SideIndicator+$P)] =$result.$P}
|
||||
}
|
||||
}
|
||||
[Pscustomobject]$hash
|
||||
}
|
||||
|
||||
#Sort by reference row number, and fill in any blanks in the difference-row column.
|
||||
$ExpandedDiff = $ExpandedDiff | Sort-Object -Property "<row"
|
||||
for ($i = 1; $i -lt $ExpandedDiff.Count; $i++) {if (-not $ExpandedDiff[$i].">row") {$ExpandedDiff[$i].">row" = $ExpandedDiff[$i-1].">row" } }
|
||||
#Sort by difference row number, and fill in any blanks in the reference-row column.
|
||||
$ExpandedDiff = $ExpandedDiff | Sort-Object -Property ">row"
|
||||
for ($i = 1; $i -lt $ExpandedDiff.Count; $i++) {if (-not $ExpandedDiff[$i]."<row") {$ExpandedDiff[$i]."<row" = $ExpandedDiff[$i-1]."<row" } }
|
||||
|
||||
#if we had to put the equal rows back, take them out; sort, make sure all the columns are present in row 1 so the grid puts them in, and output.
|
||||
if ( $ExcludeDifferent) {$ExpandedDiff = $ExpandedDiff.where({$_.side -eq "=="}) | Sort-Object -Property "<row" ,">row" }
|
||||
elseif ( $IncludeEqual) {$ExpandedDiff = $ExpandedDiff | Sort-Object -Property "<row" ,">row" }
|
||||
else {$ExpandedDiff = $ExpandedDiff.where({$_.side -ne "=="}) | Sort-Object -Property "<row" ,">row" }
|
||||
$ExpandedDiff | Update-FirstObjectProperties | Out-GridView -Title "Comparing $Referencefile::$worksheet1 (<=) with $Differencefile::$WorkSheet2 (=>)"
|
||||
}
|
||||
elseif ($GridView ) {Write-Warning -Message "To use -GridView you must specify -Key and it must match one of the included properties." }
|
||||
elseif (-not $PassThru) {return ($diff | Select-Object -Property (@(@{n="_Side";e={$_.SideIndicator}},"_File" ,"_Sheet","_Row") + $propList))}
|
||||
if ( $PassThru) {return $diff }
|
||||
}
|
||||
221
Export-Excel.ps1
221
Export-Excel.ps1
@@ -2,99 +2,98 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
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 Path
|
||||
Path to a new or existing .XLSX file
|
||||
Path to a new or existing .XLSX file.
|
||||
.PARAMETER ExcelPackage
|
||||
An object representing an Excel Package - usually this is returned by specifying -Passthru allowing multiple commands to work on the same Workbook without saving and reloading each time.
|
||||
.PARAMETER WorkSheetName
|
||||
The name of a sheet within the workbook - "Sheet1" by default
|
||||
The name of a sheet within the workbook - "Sheet1" by default.
|
||||
.PARAMETER ClearSheet
|
||||
If specified Export-Excel will remove any existing worksheet with the selected name. The Default behaviour is to overwrite cells in this sheet as needed (but leaving non-overwritten ones in place)
|
||||
If specified Export-Excel will remove any existing worksheet with the selected name. The Default behaviour is to overwrite cells in this sheet as needed (but leaving non-overwritten ones in place).
|
||||
.PARAMETER Append
|
||||
If specified data will be added to the end of an existing sheet, using the same column headings.
|
||||
.PARAMETER TargetData
|
||||
Data to insert onto the worksheet - this is often provided from the pipeline.
|
||||
.PARAMETER ExcludeProperty
|
||||
Specifies 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
|
||||
Text of a title to be placed in Cell A1.
|
||||
.PARAMETER TitleBold
|
||||
Sets the title in boldface type
|
||||
Sets the title in boldface type.
|
||||
.PARAMETER TitleSize
|
||||
Sets the point size for the title
|
||||
Sets the point size for the title.
|
||||
.PARAMETER TitleBackgroundColor
|
||||
Sets the cell background color for the title cell
|
||||
Sets the cell background color for the title cell.
|
||||
.PARAMETER TitleFillPattern
|
||||
Sets the fill pattern for the title cell
|
||||
Sets the fill pattern for the title cell.
|
||||
.PARAMETER Password
|
||||
Sets password protection on the workbook
|
||||
Sets password protection on the workbook.
|
||||
.PARAMETER IncludePivotTable
|
||||
Adds a Pivot table using the data in the worksheet
|
||||
Adds a Pivot table using the data in the worksheet.
|
||||
.PARAMETER PivotRows
|
||||
Name(s) columns from the spreadhseet which will provide 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 provide 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
|
||||
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,
|
||||
HashTable(s) with Sheet PivotTows, PivotColumns, PivotData, IncludePivotChart and ChartType values to make it easier to specify a definition or multiple Pivots.
|
||||
.PARAMETER IncludePivotChart,
|
||||
Include a chart with the Pivot table - implies Include Pivot Table.
|
||||
.PARAMETER NoLegend
|
||||
Exclude the legend from the pivot chart
|
||||
Exclude the legend from the pivot chart.
|
||||
.PARAMETER ShowCategory
|
||||
Add category labels to the pivot chart
|
||||
Add category labels to the pivot chart.
|
||||
.PARAMETER ShowPercent
|
||||
Add Percentage labels to the pivot chart
|
||||
Add Percentage labels to the pivot chart.
|
||||
.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 BoldTopRow
|
||||
Makes the top Row boldface
|
||||
Makes the top Row boldface.
|
||||
.PARAMETER NoHeader
|
||||
Does not put field names at the top of columns
|
||||
Does not put field names at the top of columns.
|
||||
.PARAMETER RangeName
|
||||
Makes the data in the worksheet a named range
|
||||
Makes the data in the worksheet a named range.
|
||||
.PARAMETER TableName
|
||||
Makes the data in the worksheet a table with a name applies a style to it. Name must not contain spaces
|
||||
Makes the data in the worksheet a table with a name applies a style to it. Name must not contain spaces.
|
||||
.PARAMETER TableStyle
|
||||
Selects the style for the named table - defaults to 'Medium6'
|
||||
Selects the style for the named table - defaults to 'Medium6'.
|
||||
.PARAMETER ExcelChartDefinition
|
||||
A hash table containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, 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
|
||||
Name(s) of Sheet(s) to hide in the workbook.
|
||||
.PARAMETER MoveToStart
|
||||
If specified, the worksheet will be moved to the start of the workbook.
|
||||
MoveToStart takes precedence over MoveToEnd, Movebefore and MoveAfter if more than one is specified
|
||||
MoveToStart takes precedence over MoveToEnd, Movebefore and MoveAfter if more than one is specified.
|
||||
.PARAMETER MoveToEnd
|
||||
If specified, the worksheet will be moved to the end of the workbook.
|
||||
(This is the default position for newly created sheets, but this can be used to move existing sheets)
|
||||
(This is the default position for newly created sheets, but this can be used to move existing sheets.)
|
||||
.PARAMETER MoveBefore
|
||||
If specified, the worksheet will be moved before the nominated one (which can be a postion starting from 1, or a name).
|
||||
MoveBefore takes precedence over MoveAfter if both are specified
|
||||
MoveBefore takes precedence over MoveAfter if both are specified.
|
||||
.PARAMETER MoveAfter
|
||||
If specified, the worksheet will be moved after the nominated one (which can be a postion starting from 1, or a name or *).
|
||||
If * is used, the worksheet names will be examined starting with the first one sheet placed after the last sheet which comes before it alphabetically.
|
||||
If * is used, the worksheet names will be examined starting with the first one, and the sheet placed after the last sheet which comes before it alphabetically.
|
||||
.PARAMETER KillExcel
|
||||
Closes Excel - prevents errors writing to the file because Excel has it open
|
||||
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
|
||||
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
|
||||
Column to start adding data - 1 by default
|
||||
Column to start adding data - 1 by default.
|
||||
.PARAMETER FreezeTopRow
|
||||
Freezes headers etc. in the top row
|
||||
Freezes headers etc. in the top row.
|
||||
.PARAMETER FreezeFirstColumn
|
||||
Freezes titles etc. in the left column
|
||||
Freezes titles etc. in the left column.
|
||||
.PARAMETER FreezeTopRowFirstColumn
|
||||
Freezes top row and left column (equivalent to Freeze pane 2,2 )
|
||||
Freezes top row and left column (equivalent to Freeze pane 2,2 ).
|
||||
.PARAMETER FreezePane
|
||||
Freezes panes at specified coordinates (in the form RowNumber , ColumnNumber)
|
||||
Freezes panes at specified coordinates (in the form RowNumber , ColumnNumber).
|
||||
.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
|
||||
@@ -105,34 +104,34 @@
|
||||
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)
|
||||
# integer (not really needed unless you need to round numbers, Excel will use default cell properties).
|
||||
'0'
|
||||
|
||||
# integer without displaying the number 0 in the cell
|
||||
# integer without displaying the number 0 in the cell.
|
||||
'#'
|
||||
|
||||
# number with 1 decimal place
|
||||
# number with 1 decimal place.
|
||||
'0.0'
|
||||
|
||||
# number with 2 decimal places
|
||||
# number with 2 decimal places.
|
||||
'0.00'
|
||||
|
||||
# number with 2 decimal places and thousand separator
|
||||
# number with 2 decimal places and thousand separator.
|
||||
'#,##0.00'
|
||||
|
||||
# number with 2 decimal places and thousand separator and money symbol
|
||||
# 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 color for positive numbers and a red color for negative numbers. All numbers will be proceeded by 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.
|
||||
.PARAMETER PassThru
|
||||
If specified, Export-Excel returns an object representing the Excel package without saving the package first. To save it you need to call the save or Saveas method or send it back to Export-Excel
|
||||
If specified, Export-Excel returns an object representing the Excel package without saving the package first. To save it you need to call the save or Saveas method or send it back to Export-Excel.
|
||||
|
||||
.EXAMPLE
|
||||
Get-Process | Export-Excel .\Test.xlsx -show
|
||||
@@ -291,8 +290,8 @@
|
||||
Get-Process | Select-Object -Property Name,Company,Handles,CPU,VM | Export-Excel -Path .\test.xlsx -AutoSize -WorkSheetname 'sheet2'
|
||||
Export-Excel -Path .\test.xlsx -PivotTableDefinition $pt -Show
|
||||
|
||||
This example defines two pivot tables. Then it puts Service data on Sheet1 with one call to Export-Excel and Process Data on sheet2 with a second call to Export-Excel
|
||||
The thrid and final call adds the two pivot tables and opens the spreadsheet in Excel
|
||||
This example defines two pivot tables. Then it puts Service data on Sheet1 with one call to Export-Excel and Process Data on sheet2 with a second call to Export-Excel.
|
||||
The thrid and final call adds the two pivot tables and opens the spreadsheet in Excel.
|
||||
|
||||
|
||||
.EXAMPLE
|
||||
@@ -305,7 +304,7 @@
|
||||
$excel.Dispose()
|
||||
Start-Process .\test.xlsx
|
||||
|
||||
This example uses -passthrough - put service information into sheet1 of the work book and saves the excelPackageObject in $Excel
|
||||
This example uses -passthrough - put service information into sheet1 of the work book and saves the excelPackageObject in $Excel.
|
||||
It then uses the package object to apply formatting, and then saves the workbook and disposes of the object before loading the document in Excel.
|
||||
|
||||
.EXAMPLE
|
||||
@@ -425,8 +424,8 @@
|
||||
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.
|
||||
DateTime objects are always converted to a short DateTime format in Excel. When Excel loads the file,
|
||||
it applies the local format for dates. And formulas are always saved as formulas. URIs are set as hyperlinks in the file.
|
||||
|
||||
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
|
||||
@@ -495,11 +494,7 @@
|
||||
|
||||
Try {
|
||||
$script:Header = $null
|
||||
if ($append -and $clearSheet) {throw "You can't use -Append AND -ClearSheet."}
|
||||
if ($KillExcel) {
|
||||
Get-Process excel -ErrorAction Ignore | Stop-Process
|
||||
while (Get-Process excel -ErrorAction Ignore) {}
|
||||
}
|
||||
if ($Append -and $ClearSheet) {throw "You can't use -Append AND -ClearSheet."}
|
||||
|
||||
if ($PSBoundParameters.Keys.Count -eq 0 -Or $Now) {
|
||||
$Path = [System.IO.Path]::GetTempFileName() -replace '\.tmp', '.xlsx'
|
||||
@@ -514,52 +509,13 @@
|
||||
$pkg = $ExcelPackage
|
||||
$Path = $pkg.File
|
||||
}
|
||||
Else {
|
||||
$Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
|
||||
Else { $pkg = Open-ExcelPackage -Path $Path -Create -KillExcel:$KillExcel}
|
||||
|
||||
$targetPath = Split-Path $Path
|
||||
if (!(Test-Path $targetPath)) {
|
||||
Write-Debug "Base path $($targetPath) does not exist, creating"
|
||||
$null = mkdir $targetPath -ErrorAction Ignore
|
||||
}
|
||||
elseif (Test-Path $Path) {
|
||||
Write-Debug "Path '$Path' already exists"
|
||||
}
|
||||
$params = @{}
|
||||
if ($NoClobber) {Write-Warning -Message "-NoClobber parameter is no longer used" }
|
||||
foreach ($p in @("WorkSheetname","ClearSheet","MoveToStart","MoveToEnd","MoveBefore","MoveAfter")) {if ($PSBoundParameters[$p]) {$params[$p] = $PSBoundParameters[$p]}}
|
||||
$ws = $pkg | Add-WorkSheet @params
|
||||
|
||||
$pkg = New-Object OfficeOpenXml.ExcelPackage $Path
|
||||
}
|
||||
|
||||
[OfficeOpenXml.ExcelWorksheet]$ws = $pkg | Add-WorkSheet -WorkSheetname $WorkSheetname -NoClobber:$NoClobber -ClearSheet:$ClearSheet #Add worksheet doesn't take any action for -noClobber
|
||||
if ($MoveToStart) {$pkg.Workbook.Worksheets.MoveToStart($worksheetName) }
|
||||
elseif ($MoveToEnd) {$pkg.Workbook.Worksheets.MoveToEnd($worksheetName) }
|
||||
elseif ($MoveBefore) {
|
||||
if ($pkg.Workbook.Worksheets[$MoveBefore]) {
|
||||
if ($MoveBefore -is [int]) {
|
||||
$pkg.Workbook.Worksheets.MoveBefore($ws.Index, $MoveBefore)
|
||||
}
|
||||
else {$pkg.Workbook.Worksheets.MoveBefore($worksheetname, $MoveBefore)}
|
||||
}
|
||||
else {Write-Warning "Can't find worksheet '$MoveBefore'; worsheet '$WorkSheetname' will not be moved."}
|
||||
}
|
||||
elseif ($MoveAfter) {
|
||||
if ($MoveAfter = "*") {
|
||||
if ($WorkSheetname -lt $pkg.Workbook.Worksheets[1].Name) {$pkg.Workbook.Worksheets.MoveToStart($worksheetName)}
|
||||
else {
|
||||
$i = 1
|
||||
While ($i -lt $pkg.Workbook.Worksheets.Count -and $pkg.Workbook.Worksheets[$i + 1].Name -lt $worksheetname ) { $i++}
|
||||
$pkg.Workbook.Worksheets.MoveAfter($ws.Index, $i)
|
||||
}
|
||||
}
|
||||
elseif ($pkg.Workbook.Worksheets[$MoveAfter]) {
|
||||
if ($MoveAfter -is [int]) {
|
||||
$pkg.Workbook.Worksheets.MoveAfter($ws.Index, $MoveAfter)
|
||||
}
|
||||
else {
|
||||
$pkg.Workbook.Worksheets.MoveAfter($worksheetname, $MoveAfter)
|
||||
}
|
||||
}
|
||||
else {Write-Warning "Can't find worksheet '$MoveAfter'; worsheet '$WorkSheetname' will not be moved."}
|
||||
}
|
||||
$ws.View.TabSelected = $true
|
||||
foreach ($format in $ConditionalFormat ) {
|
||||
$target = "Add$($format.Formatter)"
|
||||
@@ -578,17 +534,17 @@
|
||||
Write-Debug -Message ("Appending: headers are " + ($script:Header -join ", ") + "Start row $row")
|
||||
}
|
||||
elseif ($Title) {
|
||||
#Can only add a title if not appending
|
||||
#Can only add a title if not appending!
|
||||
$Row = $StartRow
|
||||
$ws.Cells[$Row, $StartColumn].Value = $Title
|
||||
$ws.Cells[$Row, $StartColumn].Style.Font.Size = $TitleSize
|
||||
|
||||
if ($TitleBold) {
|
||||
#set title to Bold if -TitleBold was specified.
|
||||
#Set title to Bold face font if -TitleBold was specified.
|
||||
#Otherwise the default will be unbolded.
|
||||
$ws.Cells[$Row, $StartColumn].Style.Font.Bold = $True
|
||||
}
|
||||
#can only set TitleBackgroundColor if TitleFillPattern is something other than None
|
||||
#Can only set TitleBackgroundColor if TitleFillPattern is something other than None.
|
||||
if ($TitleBackgroundColor -and ($TitleFillPattern -eq 'None')) {
|
||||
$TitleFillPattern = [OfficeOpenXml.Style.ExcelFillStyle]::Solid
|
||||
}
|
||||
@@ -599,9 +555,7 @@
|
||||
}
|
||||
$Row ++ ; $startRow ++
|
||||
}
|
||||
else {
|
||||
$Row = $StartRow
|
||||
}
|
||||
else { $Row = $StartRow }
|
||||
$ColumnIndex = $StartColumn
|
||||
$firstTimeThru = $true
|
||||
$isDataTypeValueType = $false
|
||||
@@ -720,7 +674,7 @@
|
||||
$cec = $ws.Dimension.End.Column # was $script:Header.Count
|
||||
|
||||
$targetRange = $ws.Cells[$csr, $csc, $cer, $cec]
|
||||
#if we're appending data the table may already exist:
|
||||
#if we're appending data the table may already exist.
|
||||
if ($ws.Tables[$TableName]) {
|
||||
$ws.Tables[$TableName].TableXml.table.ref = $targetRange.Address
|
||||
$ws.Tables[$TableName].TableStyle = $TableStyle
|
||||
@@ -737,7 +691,7 @@
|
||||
$pivotTableDataName = $item.Key + 'PivotTableData'
|
||||
if ($item.Value.PivotFilter) {$PivotTableStartCell = "A3"} else { $PivotTableStartCell = "A1"}
|
||||
|
||||
#Make sure the Pivot table sheet doesn't already exist
|
||||
#Make sure the Pivot table sheet doesn't already exist.
|
||||
#try { $pkg.Workbook.Worksheets.Delete( $pivotTableName) } catch {}
|
||||
[OfficeOpenXml.ExcelWorksheet]$wsPivot = $pkg | Add-WorkSheet -WorkSheetname $pivotTableName -NoClobber:$NoClobber
|
||||
|
||||
@@ -751,15 +705,17 @@
|
||||
}
|
||||
#if it is a pivot for a named sheet and it doesn't exist, create it.
|
||||
elseif ($item.Value.SourceWorkSheet -and -not $wsPivot.PivotTables[$pivotTableDataName] ) {
|
||||
$workSheet = $pkg.Workbook.Worksheets.where( {$_.name -match $item.Value.SourceWorkSheet})[0] #removed find worksheet
|
||||
#find the worksheet
|
||||
$workSheet = $pkg.Workbook.Worksheets.where( {$_.name -match $item.Value.SourceWorkSheet})[0]
|
||||
if (-not $workSheet) {Write-Warning -Message "Could not find Worksheet '$($item.Value.SourceWorkSheet)' specified in pivot-table definition $($item.key)." }
|
||||
else {
|
||||
$targetDataRange = "{0}:{1}" -f $workSheet.Dimension.Start.Address, $workSheet.Dimension.End.Address
|
||||
if ($item.Value.SourceRange) { $targetdataRange = $item.Value.SourceRange }
|
||||
else { $targetDataRange = $workSheet.Dimension.Address}
|
||||
$pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells[$PivotTableStartCell], $workSheet.Cells[$targetDataRange], $pivotTableDataName)
|
||||
}
|
||||
}
|
||||
|
||||
#if we created the pivot table, set up the rows, columns and data if we didn't, put out a message 'existed' or 'error'
|
||||
#if we created the pivot table, set up the rows, columns and data if we didn't, put out a message 'existed' or 'error' .
|
||||
if ($pivotTable) {
|
||||
foreach ($Row in $item.Value.PivotRows) {
|
||||
try {$null = $pivotTable.RowFields.Add($pivotTable.Fields[$Row]) }
|
||||
@@ -1059,29 +1015,72 @@
|
||||
}
|
||||
|
||||
function New-PivotTableDefinition {
|
||||
<#
|
||||
.Synopsis
|
||||
Creates Pivot table definitons for export excel
|
||||
.Description
|
||||
Export-Excel allows a single Pivot table to be defined using the parameters -IncludePivotTable, -PivotColumns -PivotRows,
|
||||
=PivotData, -PivotFilter, -NoTotalsInPivot, -PivotDataToColumn, -IncludePivotChart and -ChartType.
|
||||
Its -PivotTableDefintion paramater allows multiple pivot tables to be defined, with additional parameters.
|
||||
New-PivotTableDefinition is a convenient way to build these definitions.
|
||||
.Example
|
||||
$pt = New-PivotTableDefinition -PivotTableName "PT1" -SourceWorkSheet "Sheet1" -PivotRows "Status" -PivotData @{Status='Count' } -PivotFilter 'StartType' -IncludePivotChart -ChartType BarClustered3D
|
||||
$Pt += New-PivotTableDefinition -PivotTableName "PT2" -SourceWorkSheet "Sheet2" -PivotRows "Company" -PivotData @{Company='Count'} -IncludePivotChart -ChartType PieExploded3D -ShowPercent -ChartTitle "Breakdown of processes by company"
|
||||
Get-Service | Select-Object -Property Status,Name,DisplayName,StartType | Export-Excel -Path .\test.xlsx -AutoSize
|
||||
Get-Process | Select-Object -Property Name,Company,Handles,CPU,VM | Export-Excel -Path .\test.xlsx -AutoSize -WorkSheetname 'sheet2'
|
||||
$excel = Export-Excel -Path .\test.xlsx -PivotTableDefinition $pt -Show
|
||||
|
||||
This is a re-work of one of the examples in Export-Excel - instead of writing out the pivot definition hash table it is built by calling New-PivotTableDefinition.
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[Alias("PivtoTableName")]#Previous typo - use alias to avoid breaking scripts
|
||||
$PivotTableName,
|
||||
#Worksheet where the data is found
|
||||
$SourceWorkSheet,
|
||||
#Address range in the worksheet e.g "A10:F20" - the first row must be column names: if not specified the whole sheet will be used/
|
||||
$SourceRange,
|
||||
#Fields to set as rows in the Pivot table
|
||||
$PivotRows,
|
||||
#A hash table in form "FieldName"="Function", where function is one of
|
||||
#Average, Count, CountNums, Max, Min, Product, None, StdDev, StdDevP, Sum, Var, VarP
|
||||
[hashtable]$PivotData,
|
||||
#Fields to set as columns in the Pivot table
|
||||
$PivotColumns,
|
||||
#Fields to use to filter in the Pivot table
|
||||
$PivotFilter,
|
||||
[Switch]$PivotDataToColumn,
|
||||
[Switch]$NoTotalsInPivot,
|
||||
#If specified a chart Will be included.
|
||||
[Switch]$IncludePivotChart,
|
||||
#Optional title for the pivot chart, by default the title omitted.
|
||||
[String]$ChartTitle,
|
||||
#Height of the chart in Pixels (400 by default)
|
||||
[int]$ChartHeight = 400 ,
|
||||
#Width of the chart in Pixels (600 by default)
|
||||
[int]$ChartWidth = 600,
|
||||
#Cell position of the top left corner of the chart, there will be this number of rows above the top edge of the chart (default is 0, chart starts at top edge of row 1).
|
||||
[Int]$ChartRow = 0 ,
|
||||
[Int]$ChartColumn = 6,
|
||||
#Cell position of the top left corner of the chart, there will be this number of cells to the left of the chart (default is 4, chart starts at left edge of column E)
|
||||
[Int]$ChartColumn = 4,
|
||||
#Vertical offset of the chart from the cell corner.
|
||||
[Int]$ChartRowOffSetPixels = 0 ,
|
||||
#Horizontal offset of the chart from the cell corner.
|
||||
[Int]$ChartColumnOffSetPixels = 0,
|
||||
#Type of chart
|
||||
[OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie',
|
||||
#If specified hides the chart legend
|
||||
[Switch]$NoLegend,
|
||||
#if specified attaches the category to slices in a pie chart : not supported on all chart types, this may give errors if applied to an unsupported type.
|
||||
[Switch]$ShowCategory,
|
||||
#If specified attaches percentages to slices in a pie chart.
|
||||
[Switch]$ShowPercent
|
||||
)
|
||||
$validDataFuntions = [system.enum]::GetNames([OfficeOpenXml.Table.PivotTable.DataFieldFunctions])
|
||||
|
||||
if ($PivotData.values.Where({$_ -notin $validDataFuntions}) ) {
|
||||
Write-Warning -Message ("Pivot data functions might not be valid, they should be one of " + ($validDataFuntions -join ", ") + ".")
|
||||
}
|
||||
|
||||
$parameters = @{} + $PSBoundParameters
|
||||
$parameters.Remove('PivotTableName')
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
. $PSScriptRoot\Import-Html.ps1
|
||||
. $PSScriptRoot\InferData.ps1
|
||||
. $PSScriptRoot\Invoke-Sum.ps1
|
||||
. $PSScriptRoot\Join-WorkSheet.ps1
|
||||
. $PSScriptRoot\Merge-Worksheet.ps1
|
||||
. $PSScriptRoot\New-ConditionalFormattingIconSet.ps1
|
||||
. $PSScriptRoot\New-ConditionalText.ps1
|
||||
@@ -53,7 +54,7 @@
|
||||
Write-Warning 'PowerShell Excel is ready, except for that functionality'
|
||||
}
|
||||
#endregion
|
||||
Function Import-Excel {
|
||||
function Import-Excel {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Create custom objects from the rows in an Excel worksheet.
|
||||
@@ -94,6 +95,12 @@ Function Import-Excel {
|
||||
.PARAMETER EndRow
|
||||
By default all rows up to the last cell in the sheet will be imported. If specified, import stops at this row.
|
||||
|
||||
.PARAMETER StartColumn
|
||||
The number of the first column to read data from (1 by default).
|
||||
|
||||
.PARAMETER EndColumn
|
||||
By default the import reads up to the last populated column, -EndColumn tells the import to stop at an earlier number.
|
||||
|
||||
.PARAMETER Password
|
||||
Accepts a string that will be used to open a password protected Excel file.
|
||||
|
||||
@@ -359,7 +366,7 @@ Function Import-Excel {
|
||||
#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"
|
||||
#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]) {
|
||||
@@ -416,38 +423,95 @@ Function Import-Excel {
|
||||
}
|
||||
|
||||
function Add-WorkSheet {
|
||||
[cmdletBinding()]
|
||||
[OutputType([OfficeOpenXml.ExcelWorksheet])]
|
||||
param(
|
||||
#TODO Use parametersets to allow a workbook to be passed instead of a package
|
||||
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
|
||||
#An object representing an Excel Package.
|
||||
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName = "Package", Position=0)]
|
||||
[OfficeOpenXml.ExcelPackage]$ExcelPackage,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string] $WorkSheetname,
|
||||
#An Excel workbook to which the Worksheet will be added - a package contains one workbook so you can use whichever fits at the time.
|
||||
[Parameter(Mandatory=$true, ParameterSetName = "WorkBook")]
|
||||
[OfficeOpenXml.ExcelWorkbook]$ExcelWorkbook,
|
||||
#The name of the worksheet 'Sheet1' by default.
|
||||
[string]$WorkSheetname = 'Sheet1',
|
||||
#If the worksheet already exists, by default it will returned, unless -ClearSheet is specified in which case it will be deleted and re-created.
|
||||
[switch]$ClearSheet,
|
||||
#If specified, the worksheet will be moved to the start of the workbook.
|
||||
#MoveToStart takes precedence over MoveToEnd, Movebefore and MoveAfter if more than one is specified.
|
||||
[Switch]$MoveToStart,
|
||||
#If specified, the worksheet will be moved to the end of the workbook.
|
||||
#(This is the default position for newly created sheets, but this can be used to move existing sheets.)
|
||||
[Switch]$MoveToEnd,
|
||||
#If specified, the worksheet will be moved before the nominated one (which can be a postion starting from 1, or a name).
|
||||
#MoveBefore takes precedence over MoveAfter if both are specified.
|
||||
$MoveBefore ,
|
||||
# If specified, the worksheet will be moved after the nominated one (which can be a postion starting from 1, or a name or *).
|
||||
# If * is used, the worksheet names will be examined starting with the first one, and the sheet placed after the last sheet which comes before it alphabetically.
|
||||
$MoveAfter ,
|
||||
#If worksheet is provided as a copy source the new worksheet will be a copy of it. The source can be in the same workbook, or in a different file.
|
||||
[OfficeOpenXml.ExcelWorksheet]$CopySource,
|
||||
#Ignored but retained for backwards compatibility.
|
||||
[Switch] $NoClobber
|
||||
)
|
||||
|
||||
$ws = $ExcelPackage.Workbook.Worksheets[$WorkSheetname]
|
||||
if($ClearSheet -and $ws) {$ExcelPackage.Workbook.Worksheets.Delete($WorkSheetname) ; $ws = $null }
|
||||
if(!$ws) {
|
||||
Write-Verbose "Add worksheet '$WorkSheetname'"
|
||||
$ws=$ExcelPackage.Workbook.Worksheets.Add($WorkSheetname)
|
||||
}
|
||||
if ($ExcelPackage -and -not $ExcelWorkbook) {$ExcelWorkbook = $ExcelPackage.Workbook}
|
||||
|
||||
$ws = $ExcelWorkbook.Worksheets[$WorkSheetname]
|
||||
if( $ws -and $ClearSheet) { $ExcelWorkbook.Worksheets.Delete($WorkSheetname) ; $ws = $null }
|
||||
if(!$ws -and $CopySource) {
|
||||
Write-Verbose -Message "Copying into worksheet '$WorkSheetname'."
|
||||
$ws=$ExcelWorkbook.Worksheets.Add($WorkSheetname, $CopySource)
|
||||
}
|
||||
elseif(!$ws) {
|
||||
Write-Verbose -Message "Adding worksheet '$WorkSheetname'."
|
||||
$ws=$ExcelWorkbook.Worksheets.Add($WorkSheetname)
|
||||
}
|
||||
else {Write-Verbose -Message "Worksheet '$WorkSheetname' already existed."}
|
||||
if ($MoveToStart) {$ExcelWorkbook.Worksheets.MoveToStart($worksheetName) }
|
||||
elseif ($MoveToEnd ) {$ExcelWorkbook.Worksheets.MoveToEnd($worksheetName) }
|
||||
elseif ($MoveBefore ) {
|
||||
if ($ExcelWorkbook.Worksheets[$MoveBefore]) {
|
||||
if ($MoveBefore -is [int]) {
|
||||
$ExcelWorkbook.Worksheets.MoveBefore($ws.Index, $MoveBefore)
|
||||
}
|
||||
else {$ExcelWorkbook.Worksheets.MoveBefore($worksheetname, $MoveBefore)}
|
||||
}
|
||||
else {Write-Warning "Can't find worksheet '$MoveBefore'; worsheet '$WorkSheetname' will not be moved."}
|
||||
}
|
||||
elseif ($MoveAfter ) {
|
||||
if ($MoveAfter = "*") {
|
||||
if ($WorkSheetname -lt $ExcelWorkbook.Worksheets[1].Name) {$ExcelWorkbook.Worksheets.MoveToStart($worksheetName)}
|
||||
else {
|
||||
$i = 1
|
||||
While ($i -lt $ExcelWorkbook.Worksheets.Count -and $ExcelWorkbook.Worksheets[$i + 1].Name -lt $worksheetname ) { $i++}
|
||||
$ExcelWorkbook.Worksheets.MoveAfter($ws.Index, $i)
|
||||
}
|
||||
}
|
||||
elseif ($ExcelWorkbook.Worksheets[$MoveAfter]) {
|
||||
if ($MoveAfter -is [int]) {
|
||||
$ExcelWorkbook.Worksheets.MoveAfter($ws.Index, $MoveAfter)
|
||||
}
|
||||
else {
|
||||
$ExcelWorkbook.Worksheets.MoveAfter($worksheetname, $MoveAfter)
|
||||
}
|
||||
}
|
||||
else {Write-Warning "Can't find worksheet '$MoveAfter'; worsheet '$WorkSheetname' will not be moved."}
|
||||
}
|
||||
return $ws
|
||||
}
|
||||
|
||||
function ConvertFrom-ExcelSheet {
|
||||
<#
|
||||
.Synopsis
|
||||
Reads an Excel file an converts the data to a delimited text file
|
||||
Reads an Excel file an converts the data to a delimited text file.
|
||||
|
||||
.Example
|
||||
ConvertFrom-ExcelSheet .\TestSheets.xlsx .\data
|
||||
Reads each sheet in TestSheets.xlsx and outputs it to the data directory as the sheet name with the extension .txt
|
||||
Reads each sheet in TestSheets.xlsx and outputs it to the data directory as the sheet name with the extension .txt.
|
||||
|
||||
.Example
|
||||
ConvertFrom-ExcelSheet .\TestSheets.xlsx .\data sheet?0
|
||||
Reads and outputs sheets like Sheet10 and Sheet20 form TestSheets.xlsx and outputs it to the data directory as the sheet name with the extension .txt
|
||||
Reads and outputs sheets like Sheet10 and Sheet20 form TestSheets.xlsx and outputs it to the data directory as the sheet name with the extension .txt.
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
|
||||
164
Join-Worksheet.ps1
Normal file
164
Join-Worksheet.ps1
Normal file
@@ -0,0 +1,164 @@
|
||||
function Join-Worksheet {
|
||||
[CmdletBinding(DefaultParameterSetName = 'Default')]
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Combines data on all the sheets in an Excel worksheet onto a single sheet.
|
||||
.DESCRIPTION
|
||||
Join worksheet can work in two main ways:
|
||||
Either Combining data which has the same layout from many pages into one, or combining pages which have nothing in common.
|
||||
In the former case the header row is copied from the first sheet and, by default, each row of data is labelled with the name of the sheet it came from.
|
||||
In the latter case -NoHeader is specified, and each copied block can have the sheet it came from placed above it as a title.
|
||||
.EXAMPLE
|
||||
foreach ($computerName in @('Server1', 'Server2', 'Server3', 'Server4')) {
|
||||
Get-Service -ComputerName $computerName | Select-Object -Property Status, Name, DisplayName, StartType |
|
||||
Export-Excel -Path .\test.xlsx -WorkSheetname $computerName -AutoSize
|
||||
}
|
||||
$ptDef =New-PivotTableDefinition -PivotTableName "Pivot1" -SourceWorkSheet "Combined" -PivotRows "Status" -PivotFilter "MachineName" -PivotData @{Status='Count'} -IncludePivotChart -ChartType BarClustered3D
|
||||
Join-Worksheet -Path .\test.xlsx -WorkSheetName combined -FromLabel "MachineName" -HideSource -AutoSize -FreezeTopRow -BoldTopRow -PivotTableDefinition $pt -Show
|
||||
|
||||
The foreach command gets the services running on four servers and exports each to its own page in Test.xlsx.
|
||||
$PtDef= creates a defintion for a single Pivot table.
|
||||
The Join-Worksheet command uses the same file and merges the results onto a sheet named "Combined". It sets a column header of "Machinename",
|
||||
this column will contain the name of the sheet the data was copied from; after copying the data to the sheet "combined", the other sheets will be hidden.
|
||||
Join-Worksheet finishes by calling export-Excel to AutoSize cells, freeze the top row and make it bold and add the Pivot table.
|
||||
|
||||
.EXAMPLE
|
||||
Get-WmiObject -Class win32_logicaldisk | select -Property DeviceId,VolumeName, Size,Freespace |
|
||||
Export-Excel -Path "$env:computerName.xlsx" -WorkSheetname Volumes
|
||||
Get-NetAdapter | Select-Object Name,InterfaceDescription,MacAddress,LinkSpeed |
|
||||
Export-Excel -Path "$env:COMPUTERNAME.xlsx" -WorkSheetname NetAdapter
|
||||
Join-Worksheet -Path "$env:COMPUTERNAME.xlsx" -WorkSheetName Summay -Title "Summary" -TitleBold -TitleSize 22 -NoHeader -LabelBlocks -AutoSize -HideSource
|
||||
|
||||
The first two command get logical disk and network card information; each type is exported to its own sheet in a workbook.
|
||||
The Join-worksheet command copies both onto a page named "Summary". Because the data is disimilar -NoHeader is specified, ensuring the whole of each page is copied.
|
||||
Specifying -LabelBlocks causes each sheet's name to become a title on the summary page above the copied data.
|
||||
The source data is hidden, a title is addded in 22 point boldface and the columns are sized to fit the data.
|
||||
#>
|
||||
param (
|
||||
# Path to a new or existing .XLSX file.
|
||||
[Parameter(ParameterSetName = "Default", Position = 0)]
|
||||
[String]$Path ,
|
||||
# An object representing an Excel Package - usually this is returned by specifying -Passthru allowing multiple commands to work on the same Workbook without saving and reloading each time.
|
||||
[Parameter(Mandatory = $true, ParameterSetName = "Package")]
|
||||
[OfficeOpenXml.ExcelPackage]$ExcelPackage,
|
||||
# The name of a sheet within the workbook where the other sheets will be joined together - "Combined" by default.
|
||||
$WorkSheetName = 'Combined',
|
||||
# If specified any pre-existing target for the joined data will be deleted and re-created; otherwise data will be appended on this sheet.
|
||||
[switch]$Clearsheet,
|
||||
#Join-Worksheet assumes each sheet has identical headers and the headers should be copied to the target sheet, unless -NoHeader is specified.
|
||||
[switch]$NoHeader,
|
||||
#If -NoHeader is NOT specified, then rows of data will be labeled with the name of the sheet they came, FromLabel is the header for this column. If it is null or empty, the labels will be omitted.
|
||||
$FromLabel = "From" ,
|
||||
#If specified, the copied blocks of data will have the name of the sheet they were copied from inserted above them as a title.
|
||||
[switch]$LabelBlocks,
|
||||
#Sizes the width of the Excel column to the maximum width needed to display all the containing data in that cell.
|
||||
[Switch]$AutoSize,
|
||||
#Freezes headers etc. in the top row.
|
||||
[Switch]$FreezeTopRow,
|
||||
#Freezes titles etc. in the left column.
|
||||
[Switch]$FreezeFirstColumn,
|
||||
#Freezes top row and left column (equivalent to Freeze pane 2,2 ).
|
||||
[Switch]$FreezeTopRowFirstColumn,
|
||||
# Freezes panes at specified coordinates (in the form RowNumber , ColumnNumber).
|
||||
[Int[]]$FreezePane,
|
||||
#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.
|
||||
[Switch]$AutoFilter,
|
||||
#Makes the top Row boldface.
|
||||
[Switch]$BoldTopRow,
|
||||
#If Specified hides the sheets that the data is copied from.
|
||||
[switch]$HideSource,
|
||||
#Text of a title to be placed in Cell A1.
|
||||
[String]$Title,
|
||||
#Sets the fill pattern for the title cell.
|
||||
[OfficeOpenXml.Style.ExcelFillStyle]$TitleFillPattern = 'None',
|
||||
#Sets the cell background color for the title cell.
|
||||
[System.Drawing.Color]$TitleBackgroundColor,
|
||||
#Sets the title in boldface type.
|
||||
[Switch]$TitleBold,
|
||||
#Sets the point size for the title.
|
||||
[Int]$TitleSize = 22,
|
||||
# Hashtable(s) with Sheet PivotRows, PivotColumns, PivotData, IncludePivotChart and ChartType values to specify a definition for one or more pivot table(s).
|
||||
[Hashtable]$PivotTableDefinition,
|
||||
# A hashtable containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts.
|
||||
[Object[]]$ExcelChartDefinition,
|
||||
#Opens the Excel file immediately after creation. Convenient for viewing the results instantly without having to search for the file first.
|
||||
[switch]$Show,
|
||||
#If specified, an object representing the unsaved Excel package will be returned, it then needs to be saved.
|
||||
[switch]$PassThru
|
||||
)
|
||||
#region get target worksheet, select it and move it to the end.
|
||||
if ($Path -and -not $ExcelPackage) {$ExcelPackage = Open-ExcelPackage -path $Path }
|
||||
$destinationSheet = Add-WorkSheet -ExcelPackage $ExcelPackage -WorkSheetname $WorkSheetName -ClearSheet:$Clearsheet
|
||||
$destinationSheet.View.TabSelected = $true
|
||||
$ExcelPackage.Workbook.Worksheets.MoveToEnd($WorkSheetName)
|
||||
#row to insert at will be 1 on a blank sheet and lastrow + 1 on populated one
|
||||
$row = (1 + $destinationSheet.Dimension.End.Row )
|
||||
#endregion
|
||||
|
||||
#region Setup title and header rows
|
||||
#Title parameters work as they do in Export-Excel .
|
||||
if ($row -eq 1 -and $Title) {
|
||||
$destinationSheet.Cells[1, 1].Value = $Title
|
||||
$destinationSheet.Cells[1, 1].Style.Font.Size = $TitleSize
|
||||
if ($TitleBold) {$destinationSheet.Cells[1, 1].Style.Font.Bold = $True }
|
||||
#Can only set TitleBackgroundColor if TitleFillPattern is something other than None.
|
||||
if ($TitleBackgroundColor -AND ($TitleFillPattern -ne 'None')) {
|
||||
$destinationSheet.Cells[1, 1].Style.Fill.PatternType = $TitleFillPattern
|
||||
$destinationSheet.Cells[1, 1].Style.Fill.BackgroundColor.SetColor($TitleBackgroundColor)
|
||||
}
|
||||
elseif ($TitleBackgroundColor) { Write-Warning "Title Background Color ignored. You must set the TitleFillPattern parameter to a value other than 'None'. Try 'Solid'." }
|
||||
$row = 2
|
||||
}
|
||||
|
||||
if (-not $noHeader) {
|
||||
#Assume every row has titles in row 1, copy row 1 from first sheet to new sheet.
|
||||
$destinationSheet.Select("A$row")
|
||||
$ExcelPackage.Workbook.Worksheets[1].cells["1:1"].Copy($destinationSheet.SelectedRange)
|
||||
if ($FromLabel ) {
|
||||
#Add a column which says where the data comes from.
|
||||
$fromColumn = ($destinationSheet.Dimension.Columns + 1)
|
||||
$destinationSheet.Cells[$row, $fromColumn].Value = $FromLabel
|
||||
}
|
||||
$row += 1
|
||||
}
|
||||
#endregion
|
||||
|
||||
foreach ($i in 1..($ExcelPackage.Workbook.Worksheets.Count - 1) ) {
|
||||
$sourceWorksheet = $ExcelPackage.Workbook.Worksheets[$i]
|
||||
#Assume row one is titles, so data itself starts at A2.
|
||||
if ($NoHeader) {$sourceRange = $sourceWorksheet.Dimension.Address}
|
||||
else {$sourceRange = $sourceWorksheet.Dimension.Address -replace "A1:", "A2:"}
|
||||
#Position insertion point/
|
||||
$destinationSheet.Select("A$row")
|
||||
if ($LabelBlocks) {
|
||||
$destinationSheet.Cells[$row, 1].value = $sourceWorksheet.Name
|
||||
$destinationSheet.Cells[$row, 1].Style.Font.Bold = $true
|
||||
$destinationSheet.Cells[$row, 1].Style.Font.Size += 2
|
||||
$row += 1
|
||||
}
|
||||
$destinationSheet.Select("A$row")
|
||||
|
||||
#And finally we're ready to copy the data.
|
||||
$sourceWorksheet.Cells[$sourceRange].Copy($destinationSheet.SelectedRange)
|
||||
#Fill in column saying where data came from.
|
||||
if ($fromColumn) { $row..$destinationSheet.Dimension.Rows | ForEach-Object {$destinationSheet.Cells[$_, $fromColumn].Value = $sourceWorksheet.Name} }
|
||||
#Update where next insertion will go.
|
||||
$row = $destinationSheet.Dimension.Rows + 1
|
||||
if ($HideSource) {$sourceWorksheet.Hidden = [OfficeOpenXml.eWorkSheetHidden]::Hidden}
|
||||
}
|
||||
|
||||
#We accept a bunch of parameters work to pass on to Export-excel ( Autosize, Autofilter, boldtopRow Freeze ); if we have any of those call export-excel otherwise close the package here.
|
||||
$params = @{} + $PSBoundParameters
|
||||
'Path', 'Clearsheet', 'NoHeader', 'FromLabel', 'LabelBlocks', 'HideSource',
|
||||
'Title', 'TitleFillPattern', 'TitleBackgroundColor', 'TitleBold', 'TitleSize' | ForEach-Object {[void]$params.Remove($_)}
|
||||
if ($params.Keys.Count) {
|
||||
$params.WorkSheetName = $WorkSheetName
|
||||
$params.ExcelPackage = $ExcelPackage
|
||||
Export-Excel @Params
|
||||
}
|
||||
else {
|
||||
Close-ExcelPackage -ExcelPackage $ExcelPackage
|
||||
$ExcelPackage.Dispose()
|
||||
$ExcelPackage = $null
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
.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 which shows all the services and their differences, and opens it in Excel
|
||||
in a workbook named services which 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.
|
||||
@@ -17,12 +17,12 @@
|
||||
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 changed size will be highlighed in the output sheet. Changes to dates or other attributes will be ignored
|
||||
Files which are added or deleted or have changed size will be highlighed in the output sheet. Changes to dates or other attributes will be ignored.
|
||||
.Example
|
||||
merge-worksheet -RefO (dir .\ImportExcel\4.0.7) -DiffO (dir .\ImportExcel\4.0.8) -Pr Length | Out-GridView
|
||||
This time no file is written and the results -which include all properties, not just length, are output and sent to Out-Gridview.
|
||||
This version uses aliases to shorten the parameters,
|
||||
(OutputFileName can be "outFile" and the sheet "OutSheet" : DifferenceObject & ReferenceObject can be DiffObject & RefObject)
|
||||
(OutputFileName can be "outFile" and the sheet "OutSheet" : DifferenceObject & ReferenceObject can be DiffObject & RefObject).
|
||||
#>
|
||||
[cmdletbinding(SupportsShouldProcess=$true)]
|
||||
Param(
|
||||
@@ -61,10 +61,12 @@
|
||||
[Parameter(ParameterSetName='C',Mandatory=$true)]
|
||||
[switch]$NoHeader,
|
||||
|
||||
#Object to compare if a worksheet is NOT being used.
|
||||
[parameter(ParameterSetName='D',Mandatory=$true)]
|
||||
[parameter(ParameterSetName='E',Mandatory=$true)]
|
||||
[Alias('RefObject')]
|
||||
$ReferenceObject ,
|
||||
#Object to compare if a worksheet is NOT being used.
|
||||
[parameter(ParameterSetName='D',Mandatory=$true,Position=1)]
|
||||
[Alias('DiffObject')]
|
||||
$DifferenceObject ,
|
||||
@@ -95,7 +97,7 @@
|
||||
[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)
|
||||
#If specified outputs the data to the pipeline (you can add -whatif so it the command only outputs to the pipeline).
|
||||
[switch]$Passthru ,
|
||||
#If specified, opens the output workbook.
|
||||
[Switch]$Show
|
||||
@@ -238,10 +240,10 @@
|
||||
[Pscustomobject]$hash
|
||||
} | Sort-Object -Property "_row"
|
||||
|
||||
#Already sorted by reference row number, fill in any blanks in the difference-row column
|
||||
#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
|
||||
#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" } }
|
||||
|
||||
@@ -278,27 +280,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
Function Merge-MulipleSheets {
|
||||
Function Merge-MultipleSheets {
|
||||
<#
|
||||
.Synopsis
|
||||
Merges worksheets into a single worksheet with differences marked up.
|
||||
.Description
|
||||
The Merge worksheet command combines 2 sheets. Merge-MultipleSheets is designed to merge more than 2.
|
||||
So if asked to merge sheets A,B,C which contain Services, with a Name, Displayname and Start mode, where "name" is treated as the key
|
||||
it calls Merge-Worksheet to merge Name, Displayname and Start mode,from sheets A and C the result has column headings
|
||||
-Row, Name, DisplayName, Startmode, C-DisplayName, C-StartMode C-Is, C-Row
|
||||
Then it calls merge-worsheet with this result and sheet B, comparing 'Name', 'Displayname' and 'Start mode' columns on each side and outputting
|
||||
_Row, Name, DisplayName, Startmode, B-DisplayName, B-StartMode B-Is, B-Row, C-DisplayName, C-StartMode C-Is, C-Row
|
||||
Any columns in the "reference" side which are not used in the comparison are appended on the right, which is we compare the sheets in reverse order
|
||||
Merge-MultipleSheets calls Merge-Worksheet to merge Name, Displayname and Start mode, from sheets A and C
|
||||
the result has column headings -Row, Name, DisplayName, Startmode, C-DisplayName, C-StartMode C-Is, C-Row
|
||||
Merge-MultipleSheets then calls Merge-Worsheet with this result and sheet B, comparing 'Name', 'Displayname' and 'Start mode' columns on each side
|
||||
which outputs _Row, Name, DisplayName, Startmode, B-DisplayName, B-StartMode B-Is, B-Row, C-DisplayName, C-StartMode C-Is, C-Row
|
||||
Any columns in the "reference" side which are not used in the comparison are appended on the right, which is we compare the sheets in reverse order.
|
||||
|
||||
The "Is" column holds "Same", "Added", "Removed" or "Changed" and is used for conditional formatting in the output sheet (this is hidden by default),
|
||||
and when the data is written to Excel the "reference" columns "DisplayName" and "Start" are renamed "A-DisplayName" and "A-Start"
|
||||
and when the data is written to Excel the "reference" columns, in this case "DisplayName" and "Start" are renamed to reflect their source,
|
||||
so become "A-DisplayName" and "A-Start".
|
||||
|
||||
Conditional formatting is also applied to the "key" column (name in this case) so the view can be filtered to rows with changes by filtering this column on color.
|
||||
|
||||
Note: the processing order can affect what is seen as a change. For example if there is an extra item in sheet B in the example above,
|
||||
Sheet C will be processed and that row and nothing will be seen to be missing. When sheet B is processed it is marked as an addition, and the conditional formatting marks
|
||||
Sheet C will be processed and that row and will not be seen to be missing. When sheet B is processed it is marked as an addition, and the conditional formatting marks
|
||||
the entries from sheet A to show that a values were added in at least one sheet.
|
||||
However of Sheet B is the reference sheet, A and C will be seen to have an item removed; and if B is processed before C, the extra item is known when C is processed and
|
||||
so C is considered to be missing that item.
|
||||
However if Sheet B is the reference sheet, A and C will be seen to have an item removed;
|
||||
and if B is processed before C, the extra item is known when C is processed and so C is considered to be missing that item.
|
||||
.Example
|
||||
dir Server*.xlsx | Merge-MulipleSheets -WorkSheetName Services -OutputFile Test2.xlsx -OutputSheetName Services -Show
|
||||
We are auditing servers and each one has a workbook in the current directory which contains a "Services" worksheet (the result of
|
||||
|
||||
@@ -5,14 +5,20 @@
|
||||
.Example
|
||||
$excel = Open-ExcelPackage -path $xlPath
|
||||
$sheet1 = $excel.Workbook.Worksheets["sheet1"]
|
||||
set-Format -Address $sheet1.Cells["E1:S1048576"], $sheet1.Cells["V1:V1048576"] -NFormat ([cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern)
|
||||
close-ExcelPackage $excel -Show
|
||||
Set-Format -Address $sheet1.Cells["E1:S1048576"], $sheet1.Cells["V1:V1048576"] -NFormat ([cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern)
|
||||
Close-ExcelPackage $excel -Show
|
||||
|
||||
This will open the file at $xlPath, select sheet1 apply formatting to two blocks of the sheet and close the package
|
||||
This will open the file at $xlPath, select sheet1 apply formatting to two blocks of the sheet and save the package, and launch it in Excel.
|
||||
#>
|
||||
[OutputType([OfficeOpenXml.ExcelPackage])]
|
||||
Param ([Parameter(Mandatory=$true)]$Path,
|
||||
[switch]$KillExcel)
|
||||
Param (
|
||||
#The Path to the file to open
|
||||
[Parameter(Mandatory=$true)]$Path,
|
||||
#If specified, any running instances of Excel will be terminated before opening the file.
|
||||
[switch]$KillExcel,
|
||||
#By default open only opens an existing file; -Create instructs it to create a new file if required.
|
||||
[switch]$Create
|
||||
)
|
||||
|
||||
if($KillExcel) {
|
||||
Get-Process -Name "excel" -ErrorAction Ignore | Stop-Process
|
||||
@@ -20,24 +26,34 @@
|
||||
}
|
||||
|
||||
$Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
|
||||
if (Test-Path $path) {New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path }
|
||||
Else {Write-Warning "Could not find $path" }
|
||||
#If -Create was not specified only open the file if it exists already (send a warning if it doesn't exist).
|
||||
if ($Create) {
|
||||
#Create the directory if required.
|
||||
$targetPath = Split-Path -Parent -Path $Path
|
||||
if (!(Test-Path -Path $targetPath)) {
|
||||
Write-Debug "Base path $($targetPath) does not exist, creating"
|
||||
$null = New-item -ItemType Directory -Path $targetPath -ErrorAction Ignore
|
||||
}
|
||||
New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path
|
||||
}
|
||||
elseif (Test-Path -Path $path) {New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path }
|
||||
else {Write-Warning "Could not find $path" }
|
||||
}
|
||||
|
||||
Function Close-ExcelPackage {
|
||||
<#
|
||||
.Synopsis
|
||||
Closes an Excel Package, saving, saving under a new name or abandoning changes and opening the file as required
|
||||
Closes an Excel Package, saving, saving under a new name or abandoning changes and opening the file in Excel as required.
|
||||
#>
|
||||
Param (
|
||||
#File to close
|
||||
#File to close.
|
||||
[parameter(Mandatory=$true, ValueFromPipeline=$true)]
|
||||
[OfficeOpenXml.ExcelPackage]$ExcelPackage,
|
||||
#Open the file
|
||||
#Open the file.
|
||||
[switch]$Show,
|
||||
#Abandon the file without saving
|
||||
#Abandon the file without saving.
|
||||
[Switch]$NoSave,
|
||||
#Save file with a new name (ignored if -NoSaveSpecified)
|
||||
#Save file with a new name (ignored if -NoSave Specified).
|
||||
$SaveAs
|
||||
)
|
||||
if ( $NoSave) {$ExcelPackage.Dispose()}
|
||||
@@ -48,3 +64,4 @@ Function Close-ExcelPackage {
|
||||
if ($show) {Start-Process -FilePath $SaveAs }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user