Skip to content

Instantly share code, notes, and snippets.

@dennythecoder
Last active March 17, 2025 02:27
Show Gist options
  • Save dennythecoder/2e86ad80eab134c9b20568464a96c102 to your computer and use it in GitHub Desktop.
Save dennythecoder/2e86ad80eab134c9b20568464a96c102 to your computer and use it in GitHub Desktop.
Naive Excel PowerShell Reader Implementation

If possible, use the module at Import Excel. It'll consider more edge cases.

I wrote this to handle a simple read of somewhat well-formed data in an Excel document like below. It has only been tested in a Windows environment without significant edge-cases considered.

SheetInventory

ID Title MyDate Quantity
1 Red Item 3/15/2025 07:00 3
2 Green Item 3/15/2025 07:00 2
3 Blue Item
4 Purple Item 3/15/2025 07:00 22

Date fields are recognized by the format in the spreadsheet. The underlying OpenXML format stores values as numbers.

The script considers multiple sheets as well.

The script only reads files and stores them into a PSCustom Object. Usage might be something like below:

Usage

$content = Grab-ExcelContent "C:\Temp\myExcelDoc.xlsx"
$sheetInventory = $content.sheetInventory
$sheetInventory | Where-Object { $_.Quantity -GT 10 }

My Code

  • You should be able to be able to copy and paste the following into your own script.
function Grab-ExcelContent($path){
    function Grab-Col($range){
        $col = ($range -split '[0-9]')[0]
        return $col
    }

    function Grab-Row($range){
        $col = ($range -split '[0-9]')[0]
        $row = $range -replace $col,""
        return $row 
    }

    function Grab-Xml($file){
        $stream = $file.Open() 
        $reader = $reader = New-Object IO.StreamReader($stream)
        $text = $reader.ReadToEnd()
        $content = [xml]$text
            $stream.Close()
            $reader.Close() 
        return $content
    }

    $base_date = (Get-Date "1/1/1900").AddDays(-2)
    function Grab-CellValue($cell){
       
        $v = $cell.v
        If($cell.t -eq "s"){
            $ss = $sharedStrings[$v].t
            If($ss."#text" -eq $null){
                return $ss
            } else {
                $ss."#text"
            } 
        } else {
            If($cell.s -ne $null){
                $xf = $xfs[$cell.s]
                $nmid = [int] $xf.numfmtid
                if($nmid -ge 14 -and $nmid -le 22){
                    return $base_date.AddDays($v)
                } else {
                    return $v
                }   
            } else { 
                return $v
            }
        }
    }
    try{
        Add-Type -Assembly "System.IO.Compression.FileSystem"
        $zip = [IO.Compression.ZipFile]::OpenRead($path)
        $sharedStringsFile = $zip.Entries | where FullName -EQ "xl/sharedStrings.xml" 
        $sharedStrings = (Grab-Xml $sharedStringsFile).sst.si
        $worksheets = $zip.Entries | where FullName -Like "xl/worksheets/*" 
        $workbook = Grab-Xml ($zip.Entries | Where FullName -EQ "xl/workbook.xml")
        $styles = Grab-Xml ($zip.Entries | Where FullName -EQ "xl/styles.xml")
        $xfs = $styles.stylesheet.cellXfs.xf
        
    } catch{
        return [pscustomobject]@{"error"="File is likely in use"}
    }
    $wsi = 0
    $content = $worksheets | foreach {
        $sheetData = (Grab-Xml $_).worksheet.sheetData
        $headers = $sheetData.row[0].c | ForEach {      
            $v = Grab-CellValue $_ 
            $c = Grab-Col $_.r
            @{$c=$v}
        }
        $rix = 0 
        $sheet = $sheetData.row | foreach {
            if($rix -eq 0) { $rix++ } else{
                $row = [PSCustomObject]@{}
                $_.c | foreach {
                    $col = Grab-Col $_.r
                    $h = $headers.$col
                    $v = Grab-CellValue $_
                    $row | Add-Member -Name $h -Type NoteProperty -Value $v
                }
                Write-Output $row
            }
        }

        if($worksheets.count -eq 1) {
            $name = $workbook.workbook.sheets.sheet.name
        } else {
            $name = $workbook.workbook.sheets.sheet[$wsi].name
            $wsi++
        } 
        Write-Output ([pscustomobject]@{$name=$sheet})
    }
    $zip.Dispose() 
    return $content
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment