{"id":8318,"date":"2014-12-02T06:00:19","date_gmt":"2014-12-02T14:00:19","guid":{"rendered":"http:\/\/www.sapien.com\/blog\/?p=8318"},"modified":"2016-04-08T16:36:01","modified_gmt":"2016-04-08T23:36:01","slug":"beyond-custom-objects-create-a-net-class","status":"publish","type":"post","link":"https:\/\/dev.sapien.com\/blog\/2014\/12\/02\/beyond-custom-objects-create-a-net-class\/","title":{"rendered":"Beyond custom objects: Create a .NET class"},"content":{"rendered":"<p>In <a href=\"http:\/\/www.sapien.com\/blog\/2014\/12\/01\/get-languagekeywords-using-convertfrom-string-on-about-topics\/\" target=\"_blank\">yesterday&#8217;s blog post<\/a>, I used the <a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113388\" target=\"_blank\">Select-String<\/a> cmdlet to capture\u00a0the table of keywords and references from the <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/hh847744.aspx\" target=\"_blank\">about_language_keywords<\/a> help topic and then used the new <a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=507579\" target=\"_blank\">ConvertFrom-String<\/a> cmdlet in the Windows PowerShell\u00a05.0 preview\u00a0to convert each row of the table into a custom object (PSCustomObject). The custom object\u00a0worked quite well. I could search, sort, format, and use the objects in Windows PowerShell.<\/p>\n<pre class=\"output\">PS C:\\&gt; $k = $table | ConvertFrom-String -TemplateContent $template \r\n    | Select-Object -Property Keyword, Reference\r\n\r\nPS C:\\&gt; $k | where Keyword -eq \"Trap\"\r\n\r\nKeyword    Reference                                                                                 \r\n-------    ---------                                                                                 \r\nTrap       about_Trap, about_Break, about_Try_Catch_Finally<\/pre>\n<p>But, it&#8217;s just a simple step from the generic custom object to a named dynamic .NET object that I can create, manage, and use in many different contexts. In this post, I&#8217;ll use the new <strong>class<\/strong> feature of the Windows PowerShell 5.0 preview to create a <strong>Keyword <\/strong> class and real Keyword objects.<\/p>\n<p>The class feature is new and works somewhat differently in the September version of the preview ($PSVersionTable.PSVersion = 5.0.9814.0) than it does in the November version (5.0.9883.0), so it&#8217;s likely to continue to change until (and even after) the official release.<\/p>\n<p>For background, see the release notes that come with the Windows PowerShell 5.0 preview and Windows PowerShell MVP Trevor Sullivan&#8217;s playful and informative blog post, <a href=\"http:\/\/trevorsullivan.net\/2014\/10\/25\/implementing-a-net-class-in-powershell-v5\/\" target=\"_blank\">Implementing a .NET Class in PowerShell v5<\/a>, in which he creates a Beer class.<\/p>\n<p>&nbsp;<\/p>\n<h2><strong>Custom Objects for Keywords<\/strong><\/h2>\n<p>The\u00a0<a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=507579\" target=\"_blank\">ConvertFrom-String<\/a> cmdlet returns custom objects with the properties that you specify, either by using the <strong>PropertyNames <\/strong>parameter or one of the template parameters, <strong>TemplateContent<\/strong> or <strong>TemplateFile<\/strong>.<\/p>\n<p>To see the properties and method of the objects that it returns, pipe the output (in this case, saved in the $k variable)\u00a0to the <a href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113322\">Get-Member<\/a> cmdlet.<\/p>\n<pre class=\"output\">PS C:\\&gt; $k | Get-Member\r\n\r\n   TypeName: Selected.System.Management.Automation.PSCustomObject\r\n\r\nName        MemberType   Definition                                                       \r\n----        ----------   ----------                                                       \r\nEquals      Method       bool Equals(System.Object obj)                                   \r\nGetHashCode Method       int GetHashCode()                                                \r\nGetType     Method       type GetType()                                                   \r\nToString    Method       string ToString()                                                \r\nKeyword     NoteProperty System.String Keyword=Begin                                      \r\nReference   NoteProperty System.String Reference=about_Functions, about_Functions_Advanced<\/pre>\n<p>The objects have the <strong>Keyword<\/strong> and <strong>Reference<\/strong> properties that we need, but the methods are pretty generic and not particularly useful.<\/p>\n<p>And, beginning in Windows PowerShell 5.0, it&#8217;s very easy to create a .NET class dynamically.<\/p>\n<h2><strong>Create a Keyword Class<\/strong><\/h2>\n<p>To create a class, begin with the <strong>class<\/strong> keyword followed by\u00a0the name of the class, which, in this case, is <strong>Keyword<\/strong>. The content of the class is enclosed in curly braces.<\/p>\n<p>To add properties to the class, just create global variables. In this case, I&#8217;ve added <strong>Name<\/strong> and <strong>Reference<\/strong> properties. (Yes, it&#8217;s that easy.)<\/p>\n<pre lang=\"PowerShell\">class Keyword \r\n{\r\n    #Properties\r\n    [String]$Name\r\n    [String]$Reference\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>To allow people to create objects that are instances of the class, add at least one constructor.<\/p>\n<p>A <strong>constructor<\/strong> is a special type of method that creates new objects. It has the same name as the class and it never returns anything (the return type is [Void], which means nothing. You can have multiple constructors, but each one has to take different numbers and types of parameters.<\/p>\n<p>In the Keyword class, I added one constructor that has two string parameters, <strong>Keyword<\/strong> and <strong>Reference<\/strong>. The commands in the constructor assign the value of the <strong>Keyword<\/strong> parameter to the <strong>Name<\/strong> property of the object and the value of the <strong>Reference<\/strong> parameter to the <strong>Reference<\/strong> property of the object.<\/p>\n<pre lang=\"PowerShell\">class Keyword \r\n{\r\n    #Properties\r\n    [String]$Name\r\n    [String]$Reference\r\n\r\n    #Constructor\r\n    Keyword (\r\n        [String]$Keyword\r\n        [String]$Reference\r\n    )\r\n    {\r\n        $this.Name = $Keyword\r\n        $this.Reference = $Reference\r\n    }\r\n}\r\n\r\n<\/pre>\n<p>Unlike the output types of functions, which are just notes, the output types of methods are enforced. If the method doesn&#8217;t return the declared output type, the method generates an error.<\/p>\n<p>The parentheses that surround the parameters are required, even when the method doesn&#8217;t take any parameters.<\/p>\n<p>As we discussed in the section about constructors, all method parameters are mandatory and positional. Each method has only one parameter set. Methods in a class can have the same name, but if they do, they must take different types and numbers of parameters.<\/p>\n<p>Did you notice the <strong>$this<\/strong> automatic variable? Like <strong>$_<\/strong> in pipelines, <strong>$this<\/strong> refers to the current object. Inside a class, when you refer to a property of the current object, you must use the $this prefix, a dot, and the property name.<\/p>\n<p>For example, to refer to the <strong>Name<\/strong> property of the current object, type:<\/p>\n<pre lang=\"PowerShell\">$this.Name<\/pre>\n<p>Requiring the <strong>$this<\/strong> variable lets you use parameters with the same names as the properties. Windows PowerShell uses the <strong>$this<\/strong> to distinguish the property from the parameter. For example, I used <strong>$Reference<\/strong> as a property of the object and a parameter of the constructor. To assign the <strong>$Reference<\/strong> parameter value to the <strong>$Reference<\/strong> property, I type:<\/p>\n<pre lang=\"PowerShell\">        # Property      = Parameter               \r\n\t$this.Reference = $Reference<\/pre>\n<p>&nbsp;<\/p>\n<h2>Create Keyword Objects<\/h2>\n<p>Now that our class is complete, we can create Keyword objects. To create an object that is an instance of a .NET class, call the <strong>New<\/strong> method. (The New-Object cmdlet doesn&#8217;t yet work for dynamically created .NET classes.) <strong>New<\/strong> is a static method &#8212; a method of the class, not of any particular object in the class. All .NET objects have the <strong>New<\/strong> method, because they inherit it from the System.Object class.<\/p>\n<p>To call a static method, type the class name in square brackets, two colons (with no intervening spaces), the method name, <strong>New<\/strong>, and a set of parentheses.<\/p>\n<p>This code calls the New method of the Keyword class:<\/p>\n<pre lang=\"PowerShell\">[Keyword]::New()<\/pre>\n<p>Inside the parentheses, call the constructor. Enter a comma-separated list of the values (omit the names) of the constructor parameters. All constructor parameters are mandatory and positional, so pay attention to the order of the values.<\/p>\n<p>The Keyword class constructor has $Keyword and $Reference string parameters (in that order). To create a keyword object for the &#8220;If&#8221; keyword, use a command like this one:<\/p>\n<pre lang=\"PowerShell\">[Keyword]::New(\"If\", \"about_If\")<\/pre>\n<p>To convert a custom object that has Keyword and Reference properties to a Keyword object, use a command like this one. It pipes the custom objects in the <strong>$k <\/strong>variable to the <strong>ForEach-Object<\/strong> cmdlet, which calls the <strong>New<\/strong> static method of the Keyword class on each object and then stores the results in the $keywords variable.<\/p>\n<pre lang=\"PowerShell\">$keywords = $k | ForEach-Object { [Keyword]::new($_.Keyword, $_.Reference)<\/pre>\n<p>Let&#8217;s see the result. The display looks just like the custom objects.<\/p>\n<pre class=\"output\">PS C:\\&gt; $keywords\r\n\r\nName         Reference                                                                                 \r\n----         ---------                                                                                 \r\nBegin        about_Functions, about_Functions_Advanced                                                 \r\nBreak        about_Break, about_Trap                                                                   \r\nCatch        about_Try_Catch_Finally                                                                   \r\nContinue     about_Continue, about_Trap<\/pre>\n<p>But, when you pipe them to the Get-Member cmdlet, you can see that the type is Keyword.<\/p>\n<pre class=\"output\">PS C:\\&gt; $keywords | Get-Member\r\n\r\n   TypeName: Keyword\r\n\r\nName        MemberType Definition                    \r\n----        ---------- ----------                    \r\nEquals      Method     bool Equals(System.Object obj)\r\nGetHashCode Method     int GetHashCode()             \r\nGetType     Method     type GetType()                \r\nToString    Method     string ToString()             \r\nName        Property   string Name {get;set;}        \r\nReference   Property   string Reference {get;set;}<\/pre>\n<p>The real value becomes evident when you add methods to the class.<\/p>\n<p>&nbsp;<\/p>\n<h2>Add a Method<\/h2>\n<p>The value of the Reference property of each keyword is a list of help topics that explain how the keyword is used in Windows PowerShell. Let&#8217;s add a GetHelp() method to the Keyword class that returns the first help topic in each reference set.<\/p>\n<p>The syntax of a class method is as follows.<\/p>\n<pre lang=\"PowerShell\">    [OutputType] Name (Parameters)<\/pre>\n<ul>\n<li><strong>OutputType<\/strong>.\u00a0Enclose in square brackets the .NET type of the objects that the method returns. If the method doesn&#8217;t return anything, that is, if the output type is <strong>[Void]<\/strong>, you can omit the output type, because Void is the default. It&#8217;s nice to include it anyway.Unlike the output types of functions, which are just notes, the output types of methods are enforced. If the method doesn&#8217;t return the declared output type, the method generates an error.<\/li>\n<li><strong>Name<\/strong>. Specify the name of the method. Typically, method names are a single camel-cased string.<\/li>\n<li><strong>Parameters<\/strong>.\u00a0The parameters\u00a0of a method\u00a0are\u00a0always mandatory and positional.\u00a0Each method has only one parameter set. Methods in a class can have the same name as other methods, but if they do, they must take different types and numbers of parameters. The parentheses that surround the parameters are required, even when the method doesn&#8217;t take any parameters.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>Here&#8217;s the code for the GetHelp() method of Keyword objects. It returns the first help topic in the Reference value as a single string. I used a <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/hh847793.aspx\" target=\"_blank\">Try-Catch<\/a> block in case the help topic isn&#8217;t on the computer. In that case, it just returns an empty string.<\/p>\n<pre lang=\"PowerShell\">    [String] GetHelp() \r\n    {\r\n        [String]$firstRef = $this.Reference.Split(\",\").Trim() | Select-Object -First 1\r\n        try \r\n        {\r\n            return Get-Help $firstRef\r\n        }\r\n        catch\r\n        {\r\n            return \"\"\r\n       }\r\n    }<\/pre>\n<p>&nbsp;<\/p>\n<p>Let&#8217;s run\u00a0the new improved Get-LanguageKeywords.ps1 script that contains the class\u00a0and save the results in the $keywords property. Now, when you pipe the Keyword objects in the $keywords variable to the Get-Member cmdlet, you can see the new GetHelp() method:<\/p>\n<pre class=\"output\">PS C:\\&gt; $keywords | Get-Member\r\n\r\n   TypeName: Keyword\r\n\r\nName        MemberType Definition                    \r\n----        ---------- ----------                    \r\nEquals      Method     bool Equals(System.Object obj)\r\nGetHashCode Method     int GetHashCode()             \r\nGetHelp     Method     string GetHelp()\r\nGetType     Method     type GetType()               \r\nToString    Method     string ToString()             \r\nName        Property   string Name {get;set;}        \r\nReference   Property   string Reference {get;set;}\r\n\r\n<\/pre>\n<p>And, you can call\u00a0the GetHelp() method of\u00a0any <strong>Keyword<\/strong> object. In these commands, I get the Keyword object whose name equals &#8220;If&#8221; and call its GetHelp() method. The method returns the\u00a0text of the about_If help topic.<\/p>\n<pre class=\"output\">PS C:\\&gt; $if = $keywords | where Name -eq \"If\"\r\nPS C:\\&gt; $if.GetHelp()\r\nTOPIC\r\n    about_If\r\n\r\nSHORT DESCRIPTION\r\n    Describes a language command you can use to run statement lists based \r\n    on the results of one or more conditional tests.\r\n\r\n\r\nLONG DESCRIPTION\r\n    You can use the If statement to run code blocks if a specified \r\n    conditional test evaluates to true. You can also specify one or more\r\n...<\/pre>\n<h2>Summary<\/h2>\n<p>It&#8217;s just a short hop from the custom object with its generic methods to a Keyword object with specialized methods.<\/p>\n<p>One word of caution. When you create a class in Windows PowerShell, it&#8217;s not saved in a library like the standard .NET classes, so it exists only in your session. To save your class, save it in a script or script module file.<\/p>\n<p><em>June Blender is a technology evangelist at SAPIEN Technologies, Inc. You can reach her at <\/em><a href=\"mailto:juneb@sapien.com\"><em>juneb@sapien.com<\/em><\/a><em> or follow her on Twitter at <\/em><a href=\"https:\/\/twitter.com\/juneb_get_help\"><em>@juneb_get_help<\/em><\/a><em>.<\/em><\/p>\n<p>&nbsp;<\/p>\n<h2>Get-Keywords.ps1<\/h2>\n<p>You can copy the script code here or download <a href=\"http:\/\/www.sapien.com\/downloads#Sample%20Scripts\/Get-Keywords.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-Keywords.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\/30\/2014 12:10 AM\r\n     Organization:     SAPIEN Technologies, Inc.\r\n     Contact:       June Blender, juneb@sapien.com, @juneb_get_help\r\n     Filename:         Get-Keywords.ps1\r\n    ===========================================================================\r\n    .SYNOPSIS\r\n        Creates Keyword objects from the Keyword-Reference table in the\r\n        about_Language_Keywords help topic.\r\n\r\n    .DESCRIPTION\r\n        The Get-Keywords.ps1 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. Then it uses a Keyword class defined in the script to\r\n        create Keyword objects.\r\n\r\n        The Script 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        Keyword\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\r\n<span style=\"color: #696969;\">class<\/span><span style=\"color: #000000;\"> Keyword{\r\n\r\n    <\/span><span style=\"color: #008000;\">#Properties<\/span><span style=\"color: #000000;\">\r\n    [<\/span><span style=\"color: #00bfff; font-weight: bold;\">String<\/span><span style=\"color: #000000;\">]<\/span><span style=\"color: #8b0000;\">$Name<\/span><span style=\"color: #000000;\">\r\n    [<\/span><span style=\"color: #00bfff; font-weight: bold;\">String<\/span><span style=\"color: #000000;\">]<\/span><span style=\"color: #8b0000;\">$Reference<\/span>\r\n\r\n    <span style=\"color: #008000;\">#Constructors<\/span>\r\n    <span style=\"color: #696969;\">Keyword<\/span><span style=\"color: #000000;\">(\r\n        [<\/span><span style=\"color: #00bfff; font-weight: bold;\">String<\/span><span style=\"color: #000000;\">]<\/span><span style=\"color: #8b0000;\">$Keyword<\/span><span style=\"color: #000000;\">,\r\n        [<\/span><span style=\"color: #00bfff; font-weight: bold;\">String<\/span><span style=\"color: #000000;\">]<\/span><span style=\"color: #8b0000;\">$Reference<\/span><span style=\"color: #000000;\">     \r\n       )\r\n    {\r\n        <\/span><span style=\"color: #8b0000;\">$this<\/span><span style=\"color: #000000;\">.Name <\/span><span style=\"color: #0000ff;\">=<\/span> <span style=\"color: #8b0000;\">$Keyword<\/span>\r\n        <span style=\"color: #8b0000;\">$this<\/span><span style=\"color: #000000;\">.Reference <\/span><span style=\"color: #0000ff;\">=<\/span> <span style=\"color: #8b0000;\">$Reference<\/span><span style=\"color: #000000;\">\r\n    }\r\n    \r\n    <\/span><span style=\"color: #008000;\">#Methods<\/span><span style=\"color: #000000;\">\r\n    [<\/span><span style=\"color: #00bfff; font-weight: bold;\">String<\/span><span style=\"color: #000000;\">] <\/span><span style=\"color: #696969;\">GetHelp<\/span><span style=\"color: #000000;\">()\r\n    {\r\n        [<\/span><span style=\"color: #00bfff; font-weight: bold;\">String<\/span><span style=\"color: #000000;\">]<\/span><span style=\"color: #8b0000;\">$firstRef<\/span> <span style=\"color: #0000ff;\">=<\/span> <span style=\"color: #8b0000;\">$this<\/span><span style=\"color: #000000;\">.Reference.Split(<\/span><span style=\"color: #800040; font-weight: bold;\">\",\"<\/span><span style=\"color: #000000;\">).Trim() <\/span><span style=\"color: #0000ff;\">|<\/span> <span style=\"color: #0000ff; font-weight: bold;\">Select-Object<\/span> <span style=\"color: #000080;\">-First<\/span> <span style=\"color: #c00000;\">1<\/span>\r\n        <span style=\"color: #408080; font-weight: bold;\">try<\/span><span style=\"color: #000000;\">\r\n        {\r\n            <\/span><span style=\"color: #408080; font-weight: bold;\">return<\/span> <span style=\"color: #0000ff; font-weight: bold;\">Get-Help<\/span> <span style=\"color: #8b0000;\">$firstRef<\/span><span style=\"color: #000000;\">\r\n        }\r\n        <\/span><span style=\"color: #408080; font-weight: bold;\">catch<\/span><span style=\"color: #000000;\">\r\n        {\r\n            <\/span><span style=\"color: #408080; font-weight: bold;\">return<\/span> <span style=\"color: #800040; font-weight: bold;\">\"\"<\/span><span style=\"color: #000000;\">\r\n        }\r\n    }\r\n}\r\n\r\n\r\n<\/span><span style=\"color: #8b0000;\">$template<\/span> <span style=\"color: #0000ff;\">=<\/span><span style=\"color: #000000;\"> @<\/span><span style=\"color: #800040; font-weight: bold;\">\"\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=\"color: #408080; font-weight: bold;\">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=\"color: #800040; font-weight: bold;\">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=\"color: #408080; font-weight: bold;\">throw<\/span> <span style=\"color: #800040; font-weight: bold;\">\"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=\"color: #0000ff; font-weight: bold;\">Select-String<\/span> <span style=\"color: #000080;\">-Pattern<\/span> <span style=\"color: #800040; font-weight: bold;\">'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=\"color: #0000ff; font-weight: bold;\">Select-string<\/span> <span style=\"color: #000080;\">-Pattern<\/span> <span style=\"color: #800040; font-weight: bold;\">'^\\s*$'<\/span><span style=\"color: #000000;\">).LineNumber <\/span><span style=\"color: #0000ff;\">|<\/span> <span style=\"color: #0000ff; font-weight: bold;\">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;\">$k<\/span> <span style=\"color: #0000ff;\">=<\/span> <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=\"color: #0000ff; font-weight: bold;\">Select-Object<\/span> <span style=\"color: #000080;\">-Property<\/span><span style=\"color: #000000;\"> Keyword, Reference\r\n\r\n<\/span><span style=\"color: #008000;\">#Convert each custom object to a Keyword object<\/span>\r\n<span style=\"color: #008000;\">#Use the null constructor and a hash table<\/span>\r\n<span style=\"color: #8b0000;\">$keywords<\/span> <span style=\"color: #0000ff;\">=<\/span> <span style=\"color: #8b0000;\">$k<\/span> <span style=\"color: #0000ff;\">|<\/span> <span style=\"color: #800040; font-weight: bold;\">foreach<\/span><span style=\"color: #000000;\"> { [Keyword]@{ Name <\/span><span style=\"color: #0000ff;\">=<\/span> <span style=\"color: #8b0000;\">$_<\/span><span style=\"color: #000000;\">.Keyword; Reference <\/span><span style=\"color: #0000ff;\">=<\/span> <span style=\"color: #8b0000;\">$_<\/span><span style=\"color: #000000;\">.Reference } }<\/span><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>In yesterday&#8217;s blog post, I used the Select-String cmdlet to capture\u00a0the table of keywords and references from the about_language_keywords help topic and then used the new ConvertFrom-String cmdlet in the Windows PowerShell\u00a05.0 preview\u00a0to convert each row of the table into a custom object (PSCustomObject). The custom object\u00a0worked quite well. I could search, sort, format, and [&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":[1033,941,25],"tags":[934,28,961],"class_list":["post-8318","post","type-post","status-publish","format-standard","hentry","category-classes-in-powershell-5-0","category-powershell-5-0","category-windows-powershell","tag-juneb","tag-powershell","tag-powershell-5-0"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/8318","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=8318"}],"version-history":[{"count":36,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/8318\/revisions"}],"predecessor-version":[{"id":8356,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/8318\/revisions\/8356"}],"wp:attachment":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/media?parent=8318"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/categories?post=8318"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/tags?post=8318"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}