Write-CommandBlogPost

Write-CommandBlogPost

Rate This
  • Comments 10

On the PowerShell team, we're big believers in the Virtuous Cycle. As you learn PowerShell more and more, things should get easier and easier to do. Inline help is a great example of the virtuous cycle, because it allows you to write scripts in such a way that they're easy to learn about the same way you learn about most things in PowerShell (with Get-Help).

The other great source of learning material for PowerShell is the Internet, and our vibrant PowerShell community. Write-CommandBlogPost is designed to give you an extra benefit of writing help for your commands: you can use this function to (almost) automatically blog them out to the rest of the world.

Write-CommandBlogPost allows you to blog out help for a command and the command's definition in a convenient format, so that you can spend more time making good scripts and less time explaining them to the rest of the world. Believe it or not, you're actually reading the output of Write-CommandBlogPost right now. If you don't like the output of Write-CommandBlogPost, you can always tweak the script to your liking.

It's things like this that make me think PowerShell is really, really, cool language.

Write-CommandBlogPost


Synopsis:

Writes a draft of a blog post for a PowerShell function or cmdlet

Syntax:

Write-CommandBlogPost [-excludeSynopsis] [-excludeSyntax] [-excludeDescription] [-excludeExamples] [-excludeNotes] [-excludeParameters] [-excludeFunction] [-includeCss] [-separator [<Object>]] [-header [<Object>]] [-footer [<Object>]] [-Verbose] [-Debug] [-ErrorAction [<ActionPreference>]] [-WarningAction [<ActionPreference>]] [-ErrorVariable [<String>]] [-WarningVariable [<String>]] [-OutVariable [<String>]] [-OutBuffer [<Int32>]] [<CommonParameters>]
Write-CommandBlogPost [-command [<Object>]] [-excludeSynopsis] [-excludeSyntax] [-excludeDescription] [-excludeExamples] [-excludeNotes] [-excludeParameters] [-excludeFunction] [-includeCss] [-separator [<Object>]] [-header [<Object>]] [-footer [<Object>]] [-Verbose] [-Debug] [-ErrorAction [<ActionPreference>]] [-WarningAction [<ActionPreference>]] [-ErrorVariable [<String>]] [-WarningVariable [<String>]] [-OutVariable [<String>]] [-OutBuffer [<Int32>]] [<CommonParameters>]

Detailed Description:

Uses the help information for a command to generate a web page containing
the synopsis, description, examples, notes, parameters, and function body
of a command, as well as an optional header and footer. This encourages
individuals and organizations who write scripts to provide good help, and
saves time in evangelizing commands that have help.

Examples:

    -------------------------- EXAMPLE 1 --------------------------





# Writes a blog post about Write-CommandBlogPost and outputs it to a file.
$header = "
On the PowerShell team, we're big believers in the Virtuous Cycle.  As you learn PowerShell more and more, things should
get easier and easier to do.  Inline help is a great example of the virtuous cycle, because it allows you to write scripts
in such a way that they're easy to learn about the same way you learn about most things in PowerShell (with Get-Help).","
The other great source of learning material for PowerShell is the Internet, and our vibrant PowerShell community.
Write-CommandBlogPost is designed to give you an extra benefit of writing help for your commands: you can use this function to (almost)
automatically blog them out to the rest of the world.","
Write-CommandBlogPost allows you to blog out help for a command and the command's definition in a convenient format,
so that you can spend more time making good scripts and less time explaining them to the rest of the world.
Believe it or not, you're actually reading the output of Write-CommandBlogPost right now.  If you don't like the output of Write-CommandBlogPost,
you can always tweak the script to your liking.
","
It's things like this that make me think PowerShell is really, really, cool language."        
Write-CommandBlogPost Write-CommandBlogPost -includeCss -header $header -footer "Hope this Helps,", "James Brundage [MSFT]" > Write-CommandBlogPost.htm
    

Command Parameters:

Name Description
command The command to write a blog post on. This can be either a cmdlet or an advanced function that has provided detailed help
excludeSynopsis If -excludeSynopsis is set, the Synopsis will be excluded from the output
excludeSyntax If -excludeSyntax is set, the Syntax will be excluded from the output
excludeDescription If -excludeDescription is set, the Description will be excluded from the output
excludeExamples If -excludeExamples is set, the examples will be excluded from the output
excludeNotes If -excludeNotes is set, the notes will be excluded from the output
excludeParameters If -excludeParameters is set, the parameters will be excluded from the output
excludeFunction If -excludeFunction is set, the function definition will be excluded from the output. This parameter has no effect if Write-CommandBlogPost is used on a compiled Cmdlet
includeCss If -includeCss is set, then the outputted HTML will include a style section to help format the webpage
separator By changing the separator, you change what separates different sections of the blog post. By default, the separator is a newline.
header The header is content that will be displayed before the blog post, such as an introductory paragraph
footer The footer is content that will be displayed after the blog post, such as a closing paragraph or signatur e

Here's Write-CommandBlogPost:

function Write-CommandBlogPost {
           
        
           
<#
.Synopsis
    Writes a draft of a blog post for a PowerShell function or cmdlet
.Description
    Uses the help information for a command to generate a web page containing
    the synopsis, description, examples, notes, parameters, and function body
    of a command, as well as an optional header and footer.  This encourages
    individuals and organizations who write scripts to provide good help, and
    saves time in evangelizing commands that have help.
.Parameter Command
    The command to write a blog post on.  This can be either a cmdlet or an 
    advanced function that has provided detailed help
.Parameter excludeSynopsis
    If -excludeSynopsis is set, the Synopsis will be excluded from the output
.Parameter excludeSyntax
    If -excludeSyntax is set, the Syntax will be excluded from the output
.Parameter excludeDescription
    If -excludeDescription is set, the Description will be excluded from the output
.Parameter excludeExamples
    If -excludeExamples is set, the examples will be excluded from the output
.Parameter excludeNotes
    If -excludeNotes is set, the notes will be excluded from the output
.Parameter excludeParameters
    If -excludeParameters is set, the parameters will be excluded from the output
.Parameter excludeFunction
    If -excludeFunction is set, the function definition will be excluded from the output.
    This parameter has no effect if Write-CommandBlogPost is used on a compiled Cmdlet
.Parameter includeCss
    If -includeCss is set, then the outputted HTML will include a style section to help format the webpage
.Parameter separator
    By changing the separator, you change what separates different sections of the blog post.
    By default, the separator is a newline.
.Parameter header
    The header is content that will be displayed before the blog post, such as an introductory paragraph
.Parameter footer
    The footer is content that will be displayed after the blog post, such as a closing paragraph or signature       
.Example
    # Writes a blog post about Write-CommandBlogPost and outputs it to a file.
    $header = "
    On the PowerShell team, we're big believers in the Virtuous Cycle.  As you learn PowerShell more and more, things should
    get easier and easier to do.  Inline help is a great example of the virtuous cycle, because it allows you to write scripts
    in such a way that they're easy to learn about the same way you learn about most things in PowerShell (with Get-Help).","
    The other great source of learning material for PowerShell is the Internet, and our vibrant PowerShell community.
    Write-CommandBlogPost is designed to give you an extra benefit of writing help for your commands: you can use this function to (almost)
    automatically blog them out to the rest of the world.","
    Write-CommandBlogPost allows you to blog out help for a command and the command's definition in a convenient format,
    so that you can spend more time making good scripts and less time explaining them to the rest of the world.
    Believe it or not, you're actually reading the output of Write-CommandBlogPost right now.  If you don't like the output of Write-CommandBlogPost,
    you can always tweak the script to your liking.
    ","
    It's things like this that make me think PowerShell is really, really, cool language."        
    Write-CommandBlogPost Write-CommandBlogPost -includeCss -header $header -footer "Hope this Helps,", "James Brundage [MSFT]" > Write-CommandBlogPost.htm
#>
    param(
        [Parameter(ParameterSetName='Command',
            ValueFromPipeline=$true,
            ValueFromRemainingArguments=$true)]
        $command,
        [switch]$excludeSynopsis,
        [switch]$excludeSyntax,
        [switch]$excludeDescription,
        [switch]$excludeExamples,
        [switch]$excludeNotes,
        [switch]$excludeParameters,
        [switch]$excludeFunction,        
        [switch]$includeCss,                
        $separator = "<BR />",
        $header,
        $footer        
    )
begin {



        if ($includeCss) {
            "
<style>
    .CmdletName { font-size:large } 
    .CmdletSynopsis { font-size:medium } 
    .CmdletDescription { font-size:medium } 
    .CmdletParameters { font-size:medium }
    th {
        font-size: medium;
        font-style: italic
    }
    table {
        border: 1
    }
</style>
            "    
        }   
    $originalOFS = $ofs
    $ofs = " "
    $realHeader = ""
    if ($header) {                    
        $header | Foreach-Object {
            $realHeader+="<p class='PostHeader'>$(($_ | Out-String -Width 10000).Trim())</p>"
        }
    }
    $realHeader    

}
process {



        foreach ($c in $command) {
            if ($c -isnot [Management.Automation.CommandInfo]) {
                $realC = Get-Command $c
            } else {
                $realC = $c
            }            
            $realC | Foreach-Object {
                $parameterSection = $null
                $synopsisSection = $null    
                $syntaxSection = $null
                $descriptionSection = $null  
                $definitionSection = $null             
                $notesSection = $null
                $exampleSection = $null                                
                $nameSection = "<p class='CmdletName'>$($_.Name)</p>"
                $help = $_ | Get-Help -ErrorAction SilentlyContinue
                if ($help) {
                    $parametersDescribed = $help.Parameters.Parameter | Where-Object { $_.Description }
                    if (-not $excludeParameters -and $parametersDescribed ) {
                        $parameterTable = $help.Parameters.Parameter | 
                            Select-Object @{
                                Name='Name'
                                Expression={$_.Name}
                            }, @{
                                Name='Description'
                                Expression={($_.Description | Out-String).Trim()}
                            } | 
                            ConvertTo-Html -Fragment                         
                        $parameterSection = "
<p class='CmdletParameters'>
Command Parameters:
<blockquote>
$parameterTable
</blockquote>
</p>
                        "                            
                    }
                    if (-not $excludeSynopsis -and $help.Synopsis) {
                        $synopsisSection = "
<p class='CmdletSynopsis'>
<b>Synopsis:</b><BR />
    <blockquote>
    $(($help.Synopsis | Out-String -Width 10000).Trim())
    </blockquote>
</p>
"    
                    }
                    if (-not $excludeSyntax -and $help.Syntax) {
                        $syntaxSection = "
<p class='CmdletSyntax'><b>Syntax:</b><BR />
<blockquote>
$([Security.SecurityElement]::Escape(($help.Syntax | Out-String -Width 10000).Trim()).Replace([Environment]::Newline, '<BR />'))
</blockquote>
</p>"
                    }
                    if (-not $excludeDescription -and $help.Description) {
                        $descriptionSection = "
<p class='CmdletDescription'><b>Detailed Description:</b><BR />
<blockquote>
$(($help.Description | Out-String -Width 10000).Trim().Replace([Environment]::Newline, '<BR />'))
</blockquote>
</p>"
                    }                    
                    if (-not $excludeNotes -and $help.AlertSet){
                        $notesSection = "
<p class='CmdletNotes'>
<b>Notes:</b><BR />
<blockquote>
$(($help.AlertSet | Out-String -Width 10000).Trim().Replace([Environment]::Newline, '<BR />'))
</blockquote>
</p>"                        
                    }
                    if (-not $excludeExamples -and $help.Examples) {
                        $exampleText = ""                        
                        $help.Examples.Example | 
                            Foreach-Object {
                            $exampleText+="
    <blockquote>
    <pre class='CmdletExample'>
    $(($_ |Out-String -Width 10000).Trim())
    </pre>
    </blockquote>
"
                        }                        
                        $exampleSection = "
<p class='Examples'>
    Examples:                            
    
    $exampleText
</p>"                         
                    }                                            
                }
                if ($_.ScriptBlock -and (-not $excludeFunction)) {
$definitionSection = "
<p>
Here's $($_.Name):
<i>
<blockquote>
    <pre class='CmdletDefinition'>
function $($_.Name) {
           $($_.ScriptBlock.ToString().Replace("&", "&amp;").Replace("<", "&lt;").Replace(">","&gt;"))
}
    </pre>
</blockquote>
</i>
</p>"                        
                }
                $oldOfs = $ofs
                $realFooter = ""
                if ($footer) {                    
                    $footer | Foreach-Object {
                        $realfooter+="<p class='Postfooter'>$(($_ | Out-String -Width 10000).Trim())</p>"
                    }
                }                
                $ofs = $separator                
                "$($nameSection,
                    $synopsisSection,
                    $syntaxSection,
                    $descriptionSection,
                    $notesSection,
                    $exampleSection,
                    $parameterSection,                    
                    $definitionSection | 
                        Where-Object { $_ })                
                "               
                $ofs =$oldOFS
            }
        }
    


}
end {

    $realFooter = ""
    if ($footer) {                    
        $footer | Foreach-Object {
            $realfooter+="<p class='Postfooter'>$(($_ | Out-String -Width 10000).Trim())</p>"
        }
    }
    $realFooter
    "<p style='font-size:xx-small'>Automatically generated with <a href='http://blogs.msdn.com/powershell/archive/tags/Write-CommandBlogPost/default.aspx'>Write-CommandBlogPost</a></p>"
    $ofs = $originalOfs 

}

}
    

Automatically generated with Write-CommandBlogPost

Leave a Comment
  • Please add 1 and 8 and type the answer here:
  • Post
  • As I said in a previous post, I've started to explore the wild world of XNA . XNA just released version

  • Why does the meta data structure that provides help information for the new advanced functions not use the xml already defined for the equivalent in Cmdlet definitions with an assembly? Wouldn't it be better to have ONE format for this type of information instead of two? Or is the format for Cmdlet help files changing as well at some time?

  • Can you post the actual file, I can seem to get the formatting correct.  Thanks

  • Great stuff ... but you have a bug in your script.

    On line 151, you wrapped your [Security.SecurityElement]::Escape(...) around the BR tag you're inserting between syntax blocks, so you instead of getting a line-break, you actually got the tag as text in the post.

  • @alexander

    There are 2 scenarios for HELP:

    Community:  

    This is the structure you see here.  We believe that XML would not have good uptake so we provided a simple syntax that we felt everyone could use.

    Product quality:

    You can provide internationalized XML help for PS1 files using the normal schema.  These files would get stored in the locale directory  just like a binary cmdlet and then you'd have to ship both the .PS1 and the help files when sharing the file (see why we didn't think this was a good choice for community sharing?)

    I'll get someone to blog how to make this work.

    Jeffrey Snover [MSFT]

    Windows Management Partner Architect

    Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell

    Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

  • @ Alexander: Both MAML help and inline help provide the same property bag you see when you save the output of Get-Help to a variable.  Go ahead and give this a whirl on some of your cmdlets with help and you'll see what I mean.  If the help came from a function, then tool will append the function definition, otherwise, you can use this to generate web documentation for your cmdlets.

    e.g. Write-CommandBlogPost Get-Command

    @ Jaykul, when did you try to download the script?   There were a few errors related to how it escaped &gt; and &lt; that I fixed earlier in the morning.

    @ Murray: I sure can post the file, but I've posted ways to programmatically pull chunks of HTML on my personal blog (http://blogs.msdn.com/MediaAndMicrocode), and will do a "bringing it all together post" soon.

    Hope this Helps,

    James Brundage [MSFT]

  • On my personal blog ( Media And Microcode ), I've been posting a series called &quot;Scripting the Web&quot;,

  • @James: It's still there now. If you look at the Syntax block, you've got a BR embedded in there, and if you look at the script that produces it, around line 151, you wrapped your [Security.SecurityElement]::Escape(...) all the way around the output of the pipe (including the BR tag you're JOINing with) so you instead of getting a line-break, you actually got the tag as text in the post.

  • I found a couple of problems with the script right off the bat--haven't even gotten to the problem that Joel "Jaykul" Bennett described.

    (1) The script assumes that $ofs is set--by default, it is not, and I had to set it to $ofs = " " in my shell before running the function

    (2) There is a typo between $examplesSection in near the top of the process block and $exampleSection in the rest of the function. I changed $examplesSection to $exampleSection and it is at least outputting HTML now.

  • @jaykul

    I thought you were referring to unescaped HTML in the function definition.  I just updated the post with a fix for the Syntax section.  Thanks!

    @tommy

    I added a begin / end clause to set $ofs.

    I corrected the typo of $examplesSection / $exampleSection.  That initialization code was only really important if you had strict mode set, so it should not have affected the output of the script.

    I also added a fix so that headers and footers are done in the begin / end section, respectively, so that you can post multiple commands with only one header or footer.

    Hope this Helps,

    James Brundage [MSFT]

Page 1 of 1 (10 items)