Disclaimer: There’s a lot of parse-by-prayer here.  You Have Been Warned.

Here’s the short form: I try to keep a change log of sorts in the comment-based help for my scripts.  (I say try because that’s sadly the first thing to be omitted when I’m in a hurry.  Code clarity is sadly second.)  It looks something like this:

<#
.NOTES
Who         What        When        Why
timdunn     v1.0        2014-01-25  What’s the point?
#>

Yes, indeed. Why do we bother?

Well, it turns out you can get the data from the ‘NOTES’ section, though not in a way that makes any sense to me.

(Get-Help $Path).AlertSet.Alert

That returns the ‘NOTES’ data.  I’m not sure it does me a lot of good, though.  Digging into it, the actual data is stored as a single string under:

(Get-Help $Path).AlertSet.Alert.GetValue(0).Text

This is, as mentioned before, a single string.  From here, we do some slice-n-dicing with Split(), –replace, and the usual other set of tools at our disposal.


function Get-VersionDataFromHelp
{
     <#    

   
.SYNOPSIS
     Get version data from NOTES section of Comment-Based-Help

    .DESCRIPTION
     (Get-Help $Path).AlertSet returns the data from the '.NOTES' subheading. 
    The first line of this data set is expected to contain the words 'who, what', 'When', and 'why'.

    .PARAMETER Path
     Script or function from which to extract data.

    .EXAMPLE
     Get-VersionDataFromHelp Get-VersionDataFromHelp

    Return version data from this function.

    .EXAMPLE
     Get-VersionDataFromHelp path/to/script.ps1

    Return version data from specified file.

    .NOTES
     who         what        when        why
     timdunn     V1.0        2014-01-25  Initial version

    #>


     param (
         [string]$Path = $MyInvocation.MyCommand.Name
     );

     # Get NOTES data from command-based-help
     ####################
     $alertData = (Get-Help $Path -ErrorAction SilentlyContinue).AlertSet.Alert;
     if (!$alertData)
     {
         Write-Warning "$($MyInvocation.MyCommand.Name) Cannot find 'NOTES' section in $Path Comment-Based-Help.  Stopping.";
         return;

     } # if (!$alertData)

     [string[]]$noteData = $alertData.GetValue(0).Text.Split("`n");
     if ($noteData.Count -le 1)
     {
         Write-Warning "$($MyInvocation.MyCommand.Name) Cannot find 'NOTES' data in $Path Comment-Based-Help.  Stopping.";
         return;

     } # if (!$noteData)

     # Extract Headers
     ####################
     $header = $noteData[0];
     @('Who', 'What', 'When', 'Why') | % {
         if ($header -notmatch $_){
             Write-Warning "$($MyInvocation.MyCommand.Name) First line of $Path Comment-Based-Help 'NOTES' data missing header '$_'.  Stopping.";
        } # if ($header -notmatch $_){
     } # @('Who', 'What', 'When', 'Why') | % {

     [string[]]$headers = $header.Split(" ") | ? { $_; } | % { (Get-Culture).TextInfo.ToTitleCase($_.ToLower()); }

     if ($headers[($headers.Count - 1)] -notmatch 'Why')
     {
         Write-Warning "$($MyInvocation.MyCommand.Name) Invalid Schema: Last column of $Path Comment-Based-Help 'NOTES' data should be 'Why'.  Stopping.";
     } # if ($headers[($headers.Count - 1)] -notmatch 'Why')
    
     # Process NOTES data
     ####################
     $noteData[1 .. ($noteData.Count - 1)] | % {
        
         $lineData = ($_ -replace "\s+", " ").Split(" ", $headers.Count);
         $object = New-Object -TypeName PsObject | Select-Object -Property $headers;
         Add-Member -InputObject $object -MemberType NoteProperty -Name Source -Value (Split-Path -Leaf $Path);
        
         foreach ($i in (0 .. ($headers.Count - 1)))
         {
             $object.($headers[$i]) = $lineData[$i];
             if (($headers[$i] -match 'When') -and !($lineData[$i] -as [DateTime]))
             { # if this line doesn't have 'When' data, stop processing.
                 return;

             } # if (($headers[$i] -match 'When') -and !($lineData[$i] -as [DateTime]))

         } # foreach ($i in (0 .. ($headers.Count - 1)))

         # return data
         $object; 

     } # $noteData[1 .. ($noteData.Count - 1)] | % {
    
} # function Get-VersionDataFromHelp