{"id":8221,"date":"2014-12-01T06:00:00","date_gmt":"2014-12-01T14:00:00","guid":{"rendered":"http:\/\/www.sapien.com\/blog\/?p=8221"},"modified":"2014-11-25T19:01:16","modified_gmt":"2014-11-26T03:01:16","slug":"get-languagekeywords-using-convertfrom-string-on-about-topics","status":"publish","type":"post","link":"https:\/\/dev.sapien.com\/blog\/2014\/12\/01\/get-languagekeywords-using-convertfrom-string-on-about-topics\/","title":{"rendered":"Get-LanguageKeywords: Using ConvertFrom-String on About Topics"},"content":{"rendered":"<p>There have been some great blog posts about the new <a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=507579\"><strong>ConvertFrom-String<\/strong><\/a> cmdlet in the Windows PowerShell 5.0 preview. My favorite learning tool is an <a href=\"https:\/\/www.youtube.com\/watch?v=Hkzd8spCfCU&amp;list=PLfeA8kIs7Coehjg9cB6foPjBojLHYQGb_&amp;index=5\">excellent talk by Windows PowerShell MVP Tobias Weltner<\/a> about the full range of text parsing options available in Windows PowerShell 3.0 \u2013 5.0. While learning, I had some fun converting my electric bill and phone bill to PowerShell objects so that I could track them, but I really wasn&#8217;t using it for work.<\/p>\n<p>Until today. <\/p>\n<p>I wanted to find out whether the new <strong>enum<\/strong> keyword in the Windows PowerShell 5.0 preview had been added to the <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/hh847744.aspx\">about_language_keywords<\/a> help topic. I ran <a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=210614\"><strong>Update-Help<\/strong><\/a> and then used the <a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113388\"><strong>Select-String<\/strong><\/a> cmdlet to search for <strong>enum<\/strong> keyword. I used the <strong>Quiet<\/strong> parameter of <strong>Select-String<\/strong>, because I wanted a $True\/$False response.<\/p>\n<pre class=\"output\">Get-Help about_language_keywords | Out-String -Stream | Select-String \"enum\" \u2013Quiet<\/pre>\n<p>You can use the same <strong>Select-String<\/strong> command on the help topic file.<\/p>\n<pre class=\"output\">dir $pshome\\en-us\\about_language_keywords.help.txt | Select-String \u201cenum\u201d -Quiet<\/pre>\n<p>Both of the commands returned <strong>$False<\/strong>, but that&#8217;s to be expected, since Windows PowerShell 5.0 is still in preview.<\/p>\n<p>But, I&#8217;d really like to be able to manage those keywords in the same way that I manage type accelerators.<\/p>\n<pre class=\"output\">[PSObject].Assembly.GetType(\u201cSystem.Management.Automation.TypeAccelerators\u201d)::get<\/pre>\n<p>Or,\u00a0 check for approved verbs:<\/p>\n<pre class=\"output\">Get-Verb<\/pre>\n<p>In short, I wanted to create a keyword object that I could use in Windows PowerShell. There&#8217;s a table in the about_language_keywords help file, but I couldn&#8217;t find the contents of that table anywhere else. The About topics are just text files and the table is just a series of text strings, so I looked in my handy toolkit and pulled out <strong>ConvertFrom-String<\/strong>.<\/p>\n<h2>Isolate the Keyword Table<\/h2>\n<p>The brand-new <strong>ConvertFrom-String<\/strong> cmdlet takes text strings and returns custom objects (<a href=\"http:\/\/powershell.org\/wp\/2013\/04\/24\/pscustomobject-save-puppies-and-avoid-dead-ends\/\">PSCustomObject<\/a>) in which the text strings are values of object properties.<\/p>\n<p>I tried to create a custom object for each row of the Keyword-Reference table. But when I ran <strong>ConvertFrom-String<\/strong> on the entire help topic, I got a very pleasant error message saying that it was too complex and offering to help me with it. In the meantime, I set about isolating the Keyword-Reference table in the help topic.<\/p>\n<blockquote><p><a href=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2014\/11\/image18.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border-width: 0px;\" title=\"image\" src=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2014\/11\/image_thumb18.png\" alt=\"image\" width=\"393\" height=\"489\" border=\"0\" \/><\/a><\/p><\/blockquote>\n<p>I saved the entire <strong>about_language_keywords<\/strong> help topic content in the <span style=\"color: #0000ff;\">$help<\/span> variable.<\/p>\n<pre class=\"output\">$help = Get-Content $pshome\\en-us\\about_language_keywords.help.txt<\/pre>\n<p>My first task was to find the line number of the first table entry (Begin&#8230;) in the Keyword-Reference table. I could have counted the number of lines from the top of the file or searched for &#8220;Begin&#8221;, but wanted to make the technique durable \u2013 not dependent on the current version of the file. I decided to search for the Keyword-Reference header.<\/p>\n<p>I used the <strong>Select-String<\/strong> cmdlet and a regular expression to find the Keyword-Reference table header. To get its line number, I used the <strong>LineNumber<\/strong> property of the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.powershell.commands.matchinfo(v=vs.85).aspx\">MatchInfo<\/a>object that Select-String returns. The first table entry is two lines after the header, so I added 2 and saved the line number of the first table entry in the <span style=\"color: #0000ff;\">$start<\/span> variable.<\/p>\n<pre class=\"output\">$start = (($help | Select-String -Pattern 'Keyword\\s{7,}Reference').LineNumber) + 2<\/pre>\n<p>Next, I needed the line number of the last table entry, so I looked for the first blank line after the table. (The <strong>^\\s*$<\/strong> is the regular expression for a blank line.) The last table entry is just before the blank line, so I subtracted 1 from the line number, then added the result to the <span style=\"color: #0000ff;\">$start<\/span> value to get the line number of the last table entry from the beginning of the file.<\/p>\n<pre class=\"output\">$end = $start + (($help[($start - 1) .. 999] | \r\n    Select-string -Pattern '^\\s*$').LineNumber | \r\n    Select-Object -First 1) - 1<\/pre>\n<p>With the <span style=\"color: #0000ff;\">$start<\/span> and <span style=\"color: #0000ff;\">$end<\/span> values, I&#8217;ve captured the table in the <span style=\"color: #0000ff;\">$table<\/span> variable. (Remember to subtract 1 to convert each line number to an array index.)<\/p>\n<pre class=\"output\">$table = $help[($start \u2013 1)..($end \u2013 1)]<\/pre>\n<blockquote><p><a href=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2014\/11\/image20.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border-width: 0px;\" title=\"image\" src=\"http:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2014\/11\/image_thumb20.png\" alt=\"image\" width=\"504\" height=\"349\" border=\"0\" \/><\/a><\/p><\/blockquote>\n<p>Now, the fun begins.<\/p>\n<p>&nbsp;<\/p>\n<h2>Create a template<\/h2>\n<p>The <strong>ConvertFrom-String<\/strong> cmdlet takes text strings and returns custom objects (<a href=\"http:\/\/powershell.org\/wp\/2013\/04\/24\/pscustomobject-save-puppies-and-avoid-dead-ends\/\">PSCustomObject<\/a>) in which the text strings are values of object properties. I wanted each custom object to have <strong>Keyword <\/strong>and <strong>Reference<\/strong> properties. The values of those properties are the keyword and reference strings in the table.<\/p>\n<p>(Remember that <strong>Keyword <\/strong>and <strong>Reference<\/strong> are just names that I chose. You can use any names for the properties.)<\/p>\n<pre lang=\"PowerShell\">\r\n        Keyword            Reference\r\n        -----------        -------------------------------------------\r\n\tBegin              about_Functions, about_Functions_Advanced        \r\n        Break              about_Break, about_Trap                          \r\n        Catch              about_Try_Catch_Finally                          \r\n        Continue           about_Continue, about_Trap                       \r\n        Data               about_Data_Sections                              \r\n        Do                 about_Do, about_While<\/pre>\n<p>&nbsp;<\/p>\n<p>The <span style=\"color: #0000ff;\">$help<\/span> variables contains text strings, so I needed to tell <strong>ConvertFrom-String<\/strong> which parts of each text string go with the <strong>Name <\/strong>property, which parts go with the <strong>Reference<\/strong> property, and which parts to ignore. To do that, you use a <strong><em>template<\/em><\/strong>, which is a model for the custom objects that you create. The template consists of series of labeled examples that say, in essence, I want it my objects to look like this.<\/p>\n<p>Let&#8217;s begin with the first row of the table.<\/p>\n<ul>\n<li>To map the <strong>Begin<\/strong> string to the <strong>Keyword<\/strong> property, surround the string with curly braces and prepend the property name (Keyword) and a colon (:).<\/li>\n<\/ul>\n<pre lang=\"PowerShell\">    {Keyword:Begin}   about_Functions, about_Functions_Advanced<\/pre>\n<ul>\n<li>To indicate that <strong>Keyword<\/strong> is the first property of each new object, append an asterisk (*) to the property name.<\/li>\n<\/ul>\n<pre lang=\"PowerShell\">    {Keyword*:Begin}   about_Functions, about_Functions_Advanced<\/pre>\n<ul>\n<li>Use the same process to map the <strong>Reference<\/strong> property to the reference string. (You don&#8217;t need the asterisk here.)<\/li>\n<\/ul>\n<pre lang=\"PowerShell\">    {Keyword*:Begin}   {Reference:about_Functions, about_Functions_Advanced}<\/pre>\n<ul>\n<li>Don&#8217;t label the spaces between the keyword and reference strings, because we want <strong>ConvertFrom-String<\/strong> to ignore them. <strong>ConvertFrom-String<\/strong> excludes unlabeled characters from the custom object.<\/li>\n<\/ul>\n<pre lang=\"PowerShell\">        \r\n\t{Keyword*:Begin}   {Reference:about_Functions, about_Functions_Advanced}\r\n        Break              about_Break, about_Trap                          \r\n        Catch              about_Try_Catch_Finally                          \r\n        Continue           about_Continue, about_Trap                       \r\n        Data               about_Data_Sections                              \r\n        Do                 about_Do, about_While                            \r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Because the template is a series of examples, you don&#8217;t need to label every line of the table. Label only the lines that are parsed differently from the labeled line. <strong>ConvertFrom-String<\/strong> generates parsing rules, so use your labeling to help it generate accurate rules. (To see the rules that it generates, use the <strong>Verbose<\/strong> parameter.)<\/p>\n<p>[Tip: You must label at least two lines, even when all lines are identical. Otherwise the parsing fails.]<\/p>\n<p>For example, the reference text in the Begin row includes two phrases separated by a comma. The reference text in the Catch row has just one about phrase, so I labeled it.<\/p>\n<pre lang=\"PowerShell\">        \r\n\t{Keyword*:Begin}   {Reference:about_Functions, about_Functions_Advanced}\r\n        Break              about_Break, about_Trap                          \r\n        {Keyword*:Catch}   {Reference:about_Try_Catch_Finally}\r\n        Continue           about_Continue, about_Trap                       \r\n        Data               about_Data_Sections                              \r\n        Do                 about_Do, about_While                            \r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>I also labeled <strong>Exit<\/strong>, because its reference didn&#8217;t begin with &#8220;about&#8221; and it ends with a period. And, I labeled <strong>Workflow<\/strong>, because it was the last row. (Because its rules for ending a line rely on the characters that begin the next line , <strong>ConvertFrom-String<\/strong> tends to miss the last line.) <\/p>\n<p>Then, I deleted all of the unlabeled items from the template, placed the template in a <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/hh847740.aspx\" target=\"_blank\">here-string<\/a>, and saved it in a <span style=\"color: #0000ff;\">$template<\/span> variable. You can also save a template in a text file, but using a variable in the script guarantees that the template is available to anyone who uses the script.<\/p>\n<pre lang=\"PowerShell\">$template = @\"\r\n\r\n        {Keyword*:Begin}    {Reference:about_Functions, about_Functions_Advanced}\r\n        {Keyword*:Catch}    {Reference:about_Try_Catch_Finally}\r\n        {Keyword*:Exit}     {Reference:Described in this topic.}\r\n        {Keyword*:Workflow} {Reference:about_Workflows}\r\n\"@<\/pre>\n<p>&nbsp;<\/p>\n<h2>Run ConvertFrom-String<\/h2>\n<p>The syntax of a <strong>ConvertFrom-String<\/strong> command is pretty simple. Pipe the content to be parsed to the cmdlet. Use the <strong>TemplateContent<\/strong> or <strong>TemplateFile<\/strong> parameters to identify the template.<\/p>\n<pre class=\"output\">PS C:\\>$table | ConvertFrom-String -TemplateContent $template\r\nPS C:\\>$table\r\n\r\nExtentText                                       Keyword      Reference\r\n----------                                       -------      ---------\r\nBegin about_Functions, about_Functions_Advan.. . Begin        about_Functions, about_Functions_Advanced\r\nBreak about_Break, about_Trap.. .                Break        about_Break, about_Trap\r\nCatch about_Try_Catch_Finally.. .                Catch        about_Try_Catch_Finally\r\nContinue about_Continue, about_Trap.. .          Continue     about_Continue, about_Trap\r\n...\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>In addition to the <strong>Keyword<\/strong> and <strong>Reference<\/strong> properties, the output object includes an <strong>ExtentText<\/strong> property that contains all of the labeled text strings. ExtentText is very useful for debugging <strong>ConvertFrom-String <\/strong>output, but I don&#8217;t want it in my custom object. To exclude it, I use <strong>Select-Object<\/strong> to create a custom object with only the <strong>Keyword<\/strong> and <strong>Reference<\/strong> properties.<\/p>\n<pre class=\"output\">PS C:\\>$table | ConvertFrom-String -TemplateContent $template \r\n    | Select-Object -Property Keyword, Reference\r\n\r\n    Keyword              Reference\r\n    -------              ---------\r\n    Begin                about_Functions, about_Functions_Advanced\r\n    Break                about_Break, about_Trap\r\n    Catch                about_Try_Catch_Finally\r\n    Continue             about_Continue, about_Trap\r\n    ...\r\n<\/pre>\n<p>Done!<\/p>\n<h2><\/h2>\n<h2>Examine the Custom Objects<\/h2>\n<p>Let&#8217;s examine at the custom objects that <strong>ConvertFrom-String returned.<\/strong> They have the <strong>Keyword<\/strong> and <strong>Reference<\/strong> properties that I defined.<\/p>\n<pre class=\"output\">PS C:\\> $k = $table | ConvertFrom-String -TemplateContent $template \r\n    | Select-Object -Property Keyword, Reference\r\n\r\nPS C:\\> $k | Get-Member\r\n\r\n       TypeName: Selected.System.Management.Automation.PSCustomObject\r\n\r\n    Name        MemberType   Definition                                                       \r\n    ----        ----------   ----------                                                       \r\n    Equals      Method       bool Equals(System.Object obj)                                   \r\n    GetHashCode Method       int GetHashCode()                                                \r\n    GetType     Method       type GetType()                                                   \r\n    ToString    Method       string ToString()                                                \r\n    Keyword     NoteProperty System.String Keyword=Begin                                      \r\n    Reference   NoteProperty System.String Reference=about_Functions, about_Functions_Advanced\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>And, I can use them to explore the keywords in Windows PowerShell.<\/p>\n<pre class=\"output\">PS C:\\> $k.Keyword.Contains(\"Catch\")\r\nTrue\r\n\r\nPS C:\\> $k | where Keyword -eq \"Catch\"\r\n\r\nKeyword                Reference                                                                                 \r\n-------                ---------                                                                                 \r\nCatch                  about_Try_Catch_Finally                                                                   \r\n\r\nPS C:> Get-Help ($k | where Keyword -eq \"Catch\").Reference\r\n    about_Try_Catch_Finally\r\n\r\n    <strong>PS C:\\&gt; Get-Help (($k | where Keyword -eq \"Catch\").Reference)<\/strong>\r\n    TOPIC\r\n        about_Try_Catch_Finally\r\n\r\n    SHORT DESCRIPTION\r\n        Describes how to use the Try, Catch, and Finally blocks to handle \r\n        terminating errors.\r\n\r\n    LONG DESCRIPTION\r\n        Use Try, Catch, and Finally blocks to respond to or handle terminating \r\n        errors in scripts. The Trap statement can also be used to handle \r\n    ...\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h2>Summary<\/h2>\n<p>Here&#8217;s a review of the process that I used. I wanted to grab the table from the middle of the about_language_keywords help topic and convert each line of the table into an object.<\/p>\n<ul>\n<li>I started with the content of the about_language_keywords topic.<\/li>\n<\/ul>\n<pre class=\"output\">$help = Get-Content $pshome\\en-us\\about_language_keywords.help.txt<\/pre>\n<ul>\n<li>To isolate the table in the topic, I used the <strong>Select-String<\/strong> cmdlet and the <strong>LineNumber<\/strong> property of the MatchInfo (<a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.powershell.commands.matchinfo(v=vs.85).aspx\">Microsoft.PowerShell.Commands.MatchInfo<\/a>) object that <strong>Select-String<\/strong> returns. When indexing into the help, I just subtracted 1 from the line numbers.<\/li>\n<\/ul>\n<pre class=\"output\">$start = (($help | Select-String -Pattern 'Keyword\\s{7,}Reference').LineNumber) + 2\r\n\r\n$end = $start + (($help[($start - 1) .. 999] | \r\n    Select-string -Pattern '^\\s*$').LineNumber | \r\n    Select-Object -First 1) \u2013 1\r\n\r\n$table = $help[($start - 1)..($end - 1)]<\/pre>\n<ul>\n<li>I created a <em><strong>template<\/strong><\/em> of labeled strings with <strong>Keyword<\/strong> and <strong>Reference<\/strong> properties. The labels indicate which text strings go with each property. <strong>ConvertFrom-String<\/strong> uses the examples in the template to generate rules for parsing the text strings.<\/li>\n<\/ul>\n<pre class=\"output\">$template = @\"\r\n        {Keyword*:Begin}    {Reference:about_Functions, about_Functions_Advanced}\r\n        {Keyword*:Catch}    {Reference:about_Try_Catch_Finally}\r\n        {Keyword*:Exit}     {Reference:Described in this topic.}\r\n        {Keyword*:Workflow} {Reference:about_Workflows}\r\n\"@<\/pre>\n<ul>\n<li>I ran the <strong>ConvertFrom-String<\/strong> cmdlet with the template.<\/li>\n<\/ul>\n<pre class=\"output\">$table | ConvertFrom-String -TemplateContent $template | \r\n    Select-Object -Property Keyword, Reference<\/pre>\n<p>Finally, I dropped the commands into a script as saved it. Now, I can use it repeatedly. And when the enum keywords shows up in help, I&#8217;ll have it, too.<\/p>\n<p><i>June Blender is a technology evangelist at SAPIEN Technologies, Inc. You can reach her at <a href=\"mailto:juneb@sapien.com\">juneb@sapien.com<\/a> or follow her on Twitter at <a href=\"https:\/\/twitter.com\/juneb_get_help\">@juneb_get_help<\/a>.<\/i><\/p>\n<p>&nbsp;<\/p>\n<h2>Get-LanguageKeywords.ps1<\/h2>\n<p>You can copy the script code here or download <a href=\"http:\/\/www.sapien.com\/downloads#Sample%20Scripts\/Get-LanguageKeywords.ps1.zip\" target=\"_blank\">a ZIP file<\/a> from the <a href=\"http:\/\/www.sapien.com\/downloads\" target=\"_blank\">SAPIEN Downloads<\/a> site.<\/p>\n<p>To download, sign in, in the left nav, click <strong>Sample Scripts<\/strong>, click <strong>Get-LanguageKeywords.ps1.zip<\/strong>, and in the top right corner, click <strong>Download<\/strong>.<\/p>\n<p>&nbsp;<\/p>\n<pre><span style=\"color: #008000;\">&lt;#    \r\n    .NOTES\r\n    ===========================================================================\r\n     Created with:     SAPIEN Technologies, Inc., PowerShell Studio 2014 v4.1.74\r\n     Created on:       11\/25\/2014 4:10 PM\r\n     Organization:     SAPIEN Technologies, Inc.\r\n     Contact:       June Blender, juneb@sapien.com, @juneb_get_help\r\n     Filename:         Get-LanguageKeywords.ps1\r\n    ===========================================================================\r\n    .SYNOPSIS\r\n        Creates custom objects from the Keyword-Reference table in the\r\n        about_Language_Keywords help topic.\r\n\r\n    .DESCRIPTION\r\n        The Get-Language keywords script gets the Keyword-Reference table\r\n        from the about_Language_Keywords help topic. It uses the ConvertFrom-String\r\n        cmdlet to convert each row of the table to a custom object, and returns\r\n        the custom objects.\r\n\r\n        It takes no parameters, but it requires the about_Language_Keyword help\r\n        topic and Windows PowerShell version 5.0.\r\n\r\n    .INPUTS\r\n        None\r\n\r\n    .OUTPUTS\r\n        System.Management.Automation.PSCustomObject\r\n\r\n    .EXAMPLE\r\n        $keywords = .\\Get-LanguageKeyword.ps1\r\n        \r\n        $keywords.Keyword.Contains(\"While\")\r\n        True\r\n        \r\n        $keywords.Keyword.Contains(\"Grapefruit\")\r\n        False\r\n        \r\n        $keywords | where Name -eq \"Finally\"| Format-Table -Autosize\r\n        Keyword                  Reference\r\n        ----                     ---------\r\n        Finally                  about_Try_Catch_Finally\r\n\r\n        Get-Help ($keywords | where Name -eq \"Finally\").Reference\r\n#&gt;<\/span>\r\n\r\n<span style=\"color: #008000;\">#Requires -Version 5<\/span>\r\n\r\n<span style=\"color: #8b0000;\">$template<\/span> <span style=\"color: #0000ff;\">=<\/span><span style=\"color: #000000;\"> @<\/span><span style=\"font-weight: bold; color: #800040;\">\"\r\n\r\n        {Keyword*:Begin}   {Reference:about_Functions, about_Functions_Advanced}\r\n        {Keyword*:Catch}       {Reference:about_Try_Catch_Finally}\r\n        {Keyword*:Exit}    {Reference:Described in this topic.}\r\n        {Keyword*:Workflow}          {Reference:about_Workflows}\r\n\"<\/span><span style=\"color: #000000;\">@\r\n\r\n<\/span><span style=\"font-weight: bold; color: #408080;\">if<\/span><span style=\"color: #000000;\"> (<\/span><span style=\"color: #0000ff;\">!<\/span><span style=\"color: #000000;\">(<\/span><span style=\"color: #8b0000;\">$help<\/span> <span style=\"color: #0000ff;\">=<\/span> <span style=\"font-weight: bold; color: #800040;\">dir<\/span> <span style=\"color: #8b0000;\">$pshome<\/span><span style=\"color: #000000;\">\\en-us\\about_language_keywords.help.txt))\r\n{\r\n    <\/span><span style=\"font-weight: bold; color: #408080;\">throw<\/span> <span style=\"font-weight: bold; color: #800040;\">\"Can't find the about_language_keywords help topic. Run Update-Help and try again.\"<\/span><span style=\"color: #000000;\">\r\n}\r\n\r\n<\/span><span style=\"color: #008000;\">#Get the line number of the Keyword-Reference table header<\/span>\r\n<span style=\"color: #008000;\">#Add 2 to get the line number of the first entry in the table<\/span>\r\n<span style=\"color: #8b0000;\">$start<\/span> <span style=\"color: #0000ff;\">=<\/span><span style=\"color: #000000;\"> ((<\/span><span style=\"color: #8b0000;\">$help<\/span> <span style=\"color: #0000ff;\">|<\/span> <span style=\"font-weight: bold; color: #0000ff;\">Select-String<\/span> <span style=\"color: #000080;\">-Pattern<\/span> <span style=\"font-weight: bold; color: #800040;\">'Keyword\\s{7,}Reference'<\/span><span style=\"color: #000000;\">).LineNumber) <\/span><span style=\"color: #0000ff;\">+<\/span> <span style=\"color: #c00000;\">2<\/span>\r\n\r\n<span style=\"color: #008000;\">#Get the line number of the first blank line after the table<\/span>\r\n<span style=\"color: #008000;\">#Subtract one to get the line number of the last table entry<\/span>\r\n<span style=\"color: #8b0000;\">$end<\/span> <span style=\"color: #0000ff;\">=<\/span> <span style=\"color: #8b0000;\">$start<\/span> <span style=\"color: #0000ff;\">+<\/span><span style=\"color: #000000;\"> ((<\/span><span style=\"color: #8b0000;\">$help<\/span><span style=\"color: #000000;\">[(<\/span><span style=\"color: #8b0000;\">$start<\/span> <span style=\"color: #0000ff;\">-<\/span> <span style=\"color: #c00000;\">1<\/span><span style=\"color: #000000;\">) <\/span><span style=\"color: #0000ff;\">..<\/span> <span style=\"color: #c00000;\">999<\/span><span style=\"color: #000000;\">] <\/span><span style=\"color: #0000ff;\">|<\/span> <span style=\"font-weight: bold; color: #0000ff;\">Select-string<\/span> <span style=\"color: #000080;\">-Pattern<\/span> <span style=\"font-weight: bold; color: #800040;\">'^\\s*$'<\/span><span style=\"color: #000000;\">).LineNumber <\/span><span style=\"color: #0000ff;\">|<\/span> <span style=\"font-weight: bold; color: #0000ff;\">Select-Object<\/span> <span style=\"color: #000080;\">-First<\/span> <span style=\"color: #c00000;\">1<\/span><span style=\"color: #000000;\">) <\/span><span style=\"color: #0000ff;\">-<\/span> <span style=\"color: #c00000;\">1<\/span>\r\n\r\n<span style=\"color: #008000;\">#Get only the Keyword-Reference table<\/span>\r\n<span style=\"color: #008000;\">#Subtract 1 from each line number to get the indexes<\/span>\r\n<span style=\"color: #8b0000;\">$table<\/span> <span style=\"color: #0000ff;\">=<\/span> <span style=\"color: #8b0000;\">$help<\/span><span style=\"color: #000000;\">[(<\/span><span style=\"color: #8b0000;\">$start<\/span> <span style=\"color: #0000ff;\">-<\/span> <span style=\"color: #c00000;\">1<\/span><span style=\"color: #000000;\">) <\/span><span style=\"color: #0000ff;\">..<\/span><span style=\"color: #000000;\"> (<\/span><span style=\"color: #8b0000;\">$end<\/span> <span style=\"color: #0000ff;\">-<\/span> <span style=\"color: #c00000;\">1<\/span><span style=\"color: #000000;\">)]\r\n\r\n<\/span><span style=\"color: #008000;\">#Create a custom object from the Keyword-Reference table<\/span>\r\n<span style=\"color: #8b0000;\">$table<\/span> <span style=\"color: #0000ff;\">|<\/span> <span style=\"color: #696969;\">ConvertFrom-String<\/span> <span style=\"color: #0000ff;\">-<\/span><span style=\"color: #000000;\">TemplateContent <\/span><span style=\"color: #8b0000;\">$template<\/span> <span style=\"color: #0000ff;\">|<\/span> <span style=\"font-weight: bold; color: #0000ff;\">Select-Object<\/span> <span style=\"color: #000080;\">-Property<\/span><span style=\"color: #000000;\"> Keyword, Reference\r\n<\/span><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>There have been some great blog posts about the new ConvertFrom-String cmdlet in the Windows PowerShell 5.0 preview. My favorite learning tool is an excellent talk by Windows PowerShell MVP Tobias Weltner about the full range of text parsing options available in Windows PowerShell 3.0 \u2013 5.0. While learning, I had some fun converting my [&hellip;]<\/p>\n","protected":false},"author":31,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[943,25],"tags":[934,28],"class_list":["post-8221","post","type-post","status-publish","format-standard","hentry","category-ps-version-5-0","category-windows-powershell","tag-juneb","tag-powershell"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/8221","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/users\/31"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/comments?post=8221"}],"version-history":[{"count":85,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/8221\/revisions"}],"predecessor-version":[{"id":8327,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/8221\/revisions\/8327"}],"wp:attachment":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/media?parent=8221"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/categories?post=8221"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/tags?post=8221"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}