Twas the night before Christmas, and all through the net PowerShell lovers were wondering exactly what they might get Their readers were ready, their minds were aware That more joy of CTP3 would soon be there A cmdlet, a function? What has the PowerShell team done? How about a whole module, to share scripts with everyone? Some pluggable functions, to get code to share Scripts from Write-CommandBlogPost, from PoshCode, from Everywhere And what on the morning of Christmas did they see? But eight advanced functions, to get code from you or me. To rhyme more would be wrong, so I'll just explain. I'll introduce the functions, and call them by name. Get-MarkupTag, Get-CommandPlugin, and Resolve-ShortcutFile you've seen But the CodeDownloader module will make your eyes gleam Bookmark some favorite posts, or something on Posh-Code Put them in your favorites directory, and into a module they'll go Import-ModuleFromWeb on a directory of lines, and down comes the script Into a module named anything you see fit. And if your blog doesn't work with the toy Then write a plugin for Get-Code, and share in the joy. While rhyming is cute, for explanations it won't do So here's each script from the module, for every one of you. And if copying and pasting is too much to ask Download the attached zip file, and you're about done with your task.
Add some of the recent blog posts to your favorites, or some scripts from poshcode, and try using Import-ModuleFromWeb to import some scripts from the web into a module.
Import-ModuleFromWeb
Detailed Description:
Takes a base directory containing directories full of links to post. Each directory containing links is made into a module using Get-Code Extracts the link from a .url file and then passes the links to Get-Code (a pluggable function, please try writing one of your own). Get-Code extracts out code snippets from the web pages. Each function is placed in its own file and a module is generated that dot sources each function.
Here's Import-ModuleFromWeb:
function Import-ModuleFromWeb { <# .Synopsis Takes a directory of web links and outputs .Description Takes a base directory containing directories full of links to post. Each directory containing links is made into a module using Get-Code Extracts the link from a .url file and then passes the links to Get-Code (a pluggable function, please try writing one of your own). Get-Code extracts out code snippets from the web pages. Each function is placed in its own file and a module is generated that dot sources each function. .Example Get-ChildItem $env:UserProfile\Favorites\Code\PowerShell\Modules | New-ModuleFromWeb -force .Link Resolve-ShortcutFile .Link Get-Code .Parameter directory The name of the directory full of links to download and import .Parameter force If this is not set, will not overwrite an existing module directory .Parameter importToSession If this is set, Import-Module will be called on the newly created module #> param( [Parameter( ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position = 0)] [Alias("FullName")] [string] $directory, [switch]$force, [switch]$importToSession ) begin { if (-not (Get-Command Resolve-ShortcutFile -errorAction SilentlyContinue)) { throw $error[0] } if (-not (Get-Command Get-Code -errorAction SilentlyContinue)) { throw $error[0] } } process { $item = Get-Item $directory if ($item.PSIsContainer) { $moduleName = $item.Name if (Test-Path $env:UserProfile\Documents\WindowsPowerShell\Modules\$moduleName) { if (-not $force) { Write-Error "$moduleName already exists, use -force to overwrite" return } } $modulePath = "$env:UserProfile\Documents\WindowsPowerShell\Modules\$moduleName" $folder = New-Item $modulePath -force -type Directory $moduleText = "" Get-ChildItem $item -Recurse -Filter *.url | Resolve-ShortcutFile | Get-Code | Foreach-Object { $functionFile = $_.FunctionName + '.ps1' $_.ScriptBlock | Out-File (Join-Path $modulePath $functionFile) -Force $moduleText += ". `$psScriptRoot\$functionFile " } $moduleText | Out-File (Join-Path $modulePath "$moduleName.psm1") -Force if ($importToSession) { Import-Module $moduleName -Force } } } }
Automatically generated with Write-CommandBlogPost
Import-CodeSnippet
Takes a block of text containing a single PowerShell function definition (e.g. function foo($bar) { $bar } ) and extracts out the name of the function and returns a script block that can be used to define the function. The function will not be registered by using Import-CodeSnippet. To register functions with Import-CodeSnippet use: Import-CodeSnippet | Foreach-Object { . $_.ScriptBlock }
Here's Import-CodeSnippet:
function Import-CodeSnippet { <# .Synopsis Parses text to extract out a function name, and returns the script block to create the function .Description Takes a block of text containing a single PowerShell function definition (e.g. function foo($bar) { $bar } ) and extracts out the name of the function and returns a script block that can be used to define the function. The function will not be registered by using Import-CodeSnippet. To register functions with Import-CodeSnippet use: Import-CodeSnippet | Foreach-Object { . $_.ScriptBlock } .Parameter text A block of text containing a single PowerShell function definition .Example $text = 'function foo() { }' $text | Import-CodeSnippet #> param( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [Alias("ScriptBlock")] [string]$text ) process { if (-not $text) { return } # PowerShell can now create generic types. # We'll use this one to collect the parse errors. $err = New-Object System.Collections.ObjectModel.Collection`[System.Management.Automation.PSParseError] $tokens = [Management.Automation.PSParser]::Tokenize($text, [ref]$err) foreach ($e in $err) { Write-Error -message $e.Message ` -category ParserError ` -errorID $e.Message ` -targetObject $e.Token } $c = 0; $tokens | Where-Object { $_.Type -eq "Keyword" -and $_.Content -eq "Function" $c++ } | Foreach-Object { $tokens[$c].Content | Select-Object @{ Name='FunctionName' Expression={$_} }, @{ Name='ScriptBlock' Expression={[ScriptBlock]::Create($text)} } } } }
Get-Code
Get-Code is a pluggable function. It looks for all other functions named GetCodeFrom* that have a parameter, Text, and calls each of these functions with a block of text in order to extract code any number of possible sources. If a Get-Code plugin does not find code within the text it should return nothing. If code is found, it should return the chunk of text containing the function declaration. This text will then be passed to Import-CodeSnippet, which will return the name of the function and a script block to declare the function.
Here's Get-Code:
function Get-Code { <# .Synopsis Uses plugins to Get-Code to extract code from a web page of block of text. .Description Get-Code is a pluggable function. It looks for all other functions named GetCodeFrom* that have a parameter, Text, and calls each of these functions with a block of text in order to extract code any number of possible sources. If a Get-Code plugin does not find code within the text it should return nothing. If code is found, it should return the chunk of text containing the function declaration. This text will then be passed to Import-CodeSnippet, which will return the name of the function and a script block to declare the function. .Parameter url If url is specified, the URL will be downloaded and the text from the URL will be passed to Get-Code .Parameter text The block of text #> [CmdletBinding(DefaultParameterSetName="Url")] param( [Parameter( ParameterSetName="Url", Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [string] $url, [Parameter( ParameterSetName="Text", Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [Alias("ScriptBlock")] $text ) begin { if (-not (Get-Command Get-CommandPlugin -errorAction SilentlyContinue)) { throw $error[0] } if (-not (Get-Command Import-CodeSnippet -errorAction SilentlyContinue)) { throw $error[0] } } process { if ($psCmdlet.ParameterSetName -eq "Url") { Write-Progress "Downloading Data" $url $downloadedText = (New-Object Net.Webclient).DownloadString($url) & $myInvocation.MyCommand -Text $downloadedText return } $myInvocation.MyCommand | Get-CommandPlugin -preposition "From" -parameters "text" | Foreach-Object { Write-Progress "Attempting to extract code" "Using $_" $extractedText = & $_ @psBoundParameters if ($extractedText) { $extractedText | Import-CodeSnippet } } } }
Get-CodeFromPoshCode
Takes text downloaded from a posting on the PowerShell Code Repository (http://www.poshcode.org) and extracts out the code segment.
Here's Get-CodeFromPoshCode:
function Get-CodeFromPoshCode { <# .Synopsis Takes text containing a web page downloaded from Poshcode.org and extracts out the function contained on the page .Description Takes text downloaded from a posting on the PowerShell Code Repository (http://www.poshcode.org) and extracts out the code segment. .Parameter text The text of the file .Example $text = (New-Object Net.Webclient).DownloadString("http://www.poshcode.org/735") Get-CodeFromPoshCode $text .Link Get-Code #> param( [Parameter(Mandatory=$true, Position=0)] [string] $text ) process { if (-not (Get-Command Get-MarkupTag -errorAction SilentlyContinue)) { throw $error[0] } Get-MarkupTag textArea $text | Where-Object { $_.Xml.Id -eq "Code" } | Foreach-Object { $_.Xml.'#text' } } }
Get-CodeFromCommandBlogPost
Takes text downloaded from a blog post generated with Write-CommandBlogPost (http://blogs.msdn.com/powershell/archive/2008/12/24/write-commandblogpost.aspx) and extracts out the code segments.
Here's Get-CodeFromCommandBlogPost:
function Get-CodeFromCommandBlogPost { <# .Synopsis Takes text containing a web page downloaded from a blog post written with Write-CommandBlogPost and extracts out the function definitions .Description Takes text downloaded from a blog post generated with Write-CommandBlogPost (http://blogs.msdn.com/powershell/archive/2008/12/24/write-commandblogpost.aspx) and extracts out the code segments. .Parameter text The text of the file .Example $text = (New-Object Net.Webclient).DownloadString("http://blogs.msdn.com/powershell/archive/2008/12/24/write-commandblogpost.aspx") Get-CodeFromPoshCode $text .Link Get-Code #> param( [Parameter(Mandatory=$true, Position=0)] [string] $text ) if (-not (Get-Command Get-MarkupTag -errorAction SilentlyContinue)) { throw $error[0] } Get-MarkupTag pre $text | Where-Object { $_.Xml.Class -eq "CmdletDefinition" } | Foreach-Object { $_.Xml.'#text' } }
Hope this Helps, and Happy Holidays :
James Brundage [MSFT]