{"id":565,"date":"2014-10-09T00:01:00","date_gmt":"2014-10-09T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2014\/10\/09\/convert-a-script-to-a-powershell-function\/"},"modified":"2023-05-16T13:33:08","modified_gmt":"2023-05-16T20:33:08","slug":"convert-a-script-to-a-powershell-function","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/convert-a-script-to-a-powershell-function\/","title":{"rendered":"Convert a Script to a PowerShell Function"},"content":{"rendered":"<p><b style=\"font-size: 12px;\">Summary<\/b><span style=\"font-size: 12px;\">: Microsoft Scripting Guy, Ed Wilson, talks about converting a script to a Windows PowerShell function.<\/span><\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. When I have a script that I think I will use more than once or twice, it often makes sense for me to convert it to a function. If it is something I will use on a regular basis, I might add that function to my Windows PowerShell profile, or I might put it into a module so that it is easily accessible.<\/p>\n<p>In fact, one of my major challenges (having written nearly 1,500 Windows PowerShell scripts) is finding and reusing scripts I wrote several years ago. Functions and modules make script reuse much more practical. For example, I think I might enjoy playing around with my ASCII encode\/decode script, so it makes sense to turn it into a function.<\/p>\n<p><b>\u00a0 \u00a0 \u00a0Note\u00a0<\/b> This post is an extension of my previous posts on the subject. You should read the following posts before\nyou read today&#8217;s:<\/p>\n<ul>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/scripting\/automatically-create-hash-tables-and-reverse-values\/\" target=\"_blank\" rel=\"noopener\">Automatically Create Hash Tables and Reverse Values<\/a><\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/scripting\/converting-words-to-ascii-numbers-and-back-with-powershell\/\" target=\"_blank\" rel=\"noopener\">Converting Words to ASCII Numbers and Back with PowerShell<\/a><\/li>\n<li><a href=\"\/b\/heyscriptingguy\/archive\/2014\/10\/08\/extend-the-encode-decode-powershell-script.aspx\" target=\"_blank\" rel=\"noopener\">Extend the Encode\/Decode PowerShell Script<\/a><\/li>\n<\/ul>\n<h2>Add the function stuff\u2026<\/h2>\n<p>The first thing to do is to add the function stuff. This includes:<\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li>The function keyword<\/li>\n<li>The script block<\/li>\n<li>Parameters<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<ul>\n<li>Help<\/li>\n<\/ul>\n<p>After using the <b>Function<\/b> keyword and assigning a name to my function, I added comment-based Help. There are three ways to do this:<\/p>\n<ul>\n<li>Use the Cmdlet Advanced Function or Cmdlet Advanced Function (Complete) snippet from the Windows PowerShell ISE<\/li>\n<li>Hand code it<\/li>\n<li>Use the <b>Add-Help<\/b> function from my Windows PowerShell ISE profile<\/li>\n<\/ul>\n<p>I used the last option because I have it pretty well customized for my needs. Here is that portion of the script:<\/p>\n<pre><code>\r\nFunction Convert-AsciiEncoding {\r\n\u00a0 &lt;#\r\n\u00a0\u00a0 .Synopsis\r\n\u00a0\u00a0\u00a0 This function reads an ascii text string, and coverts it to ascii\r\n\u00a0\u00a0\u00a0 numbers.\u00a0\r\n\r\n\u00a0\u00a0 .Description\r\n\u00a0\u00a0\u00a0 This function reads an ascii text string, and converts it to ascii\r\n\u00a0\u00a0\u00a0 numeric values. I accepts input from a text file, and outputs a\r\n\u00a0\u00a0\u00a0 text file as well.\r\n\r\n\u00a0\u00a0 .Example\r\n\u00a0\u00a0\u00a0 Convert-AsciiEncoding -path C:\\fso\\Simple.txt -filepath c:\\fso\\encodeSimple.txt -encode\r\n\u00a0\u00a0\u00a0 Reads a text file, and encodes the output into an ASCII values\r\n\r\n\u00a0\u00a0 .Example\r\n\u00a0\u00a0\u00a0 Convert-AsciiEncoding -path C:\\fso\\encodeSimple.txt -filepath c:\\fso\\decodeSimple.txt -decode\r\n\u00a0\u00a0\u00a0 Reads a text file containing ascii numeric values, and converts\r\n\u00a0\u00a0\u00a0 them to text. Writes the output to a text file\r\n\r\n\u00a0\u00a0 .Parameter Path\r\n\u00a0\u00a0\u00a0 The path to the source file - either a text file to encode, or an\r\n\u00a0\u00a0\u00a0 ascii value encoded file.\r\n\r\n\u00a0\u00a0 .Parameter FilePath\r\n\u00a0\u00a0\u00a0 The path to the output file\r\n\r\n\u00a0\u00a0 .Parameter Encode\r\n\u00a0\u00a0\u00a0 Causes the function to read the input file and encode the output file\r\n\r\n\u00a0\u00a0 .Parameter Decode\r\n\u00a0\u00a0\u00a0 Causes the function to read the encoded input file and output a decoded\r\n\u00a0\u00a0\u00a0 text file\r\n\r\n\u00a0\u00a0 .Inputs\r\n\u00a0\u00a0\u00a0 [System.String]\r\n\r\n\u00a0\u00a0 .Outputs\r\n\u00a0\u00a0\u00a0 [System.IO.FileInfo]\r\n\r\n\u00a0\u00a0 .Notes\r\n\u00a0\u00a0\u00a0 NAME:\u00a0 Convert-AsciiEncoding\r\n\u00a0\u00a0\u00a0 AUTHOR: ed wilson, msft\r\n\u00a0\u00a0\u00a0 LASTEDIT: 10\/01\/2014 14:21:22\r\n\u00a0\u00a0\u00a0 KEYWORDS: Function, Scripting Techniques, Text Files, Hash Tables\r\n\u00a0\u00a0\u00a0 HSG: HSG-10-9-2014\r\n\r\n\u00a0\u00a0 .Link\r\n\u00a0\u00a0\u00a0 https:\/\/www.ScriptingGuys.com\r\n\r\n\u00a0#Requires -Version 3.0\r\n\u00a0#&gt;\r\n<\/code><\/pre>\n<p>The next thing I do is add the <b>[cmdletbinding()]<\/b> attribute. This goes after the comment-based Help, but before the <strong>Parameters<\/strong> section. This gives me easy access to things like common parameters. Here is the <b>[cmdletbinding()]<\/b> attribute and the <strong>Parameter<\/strong> section of the script:<\/p>\n<pre><code>\r\n[cmdletbinding()]\r\nParam(\r\n\u00a0\u00a0\u00a0 [string]$path,\r\n\u00a0\u00a0\u00a0 [string]$filepath,\r\n\u00a0\u00a0\u00a0 [switch]$encode,\r\n\u00a0\u00a0\u00a0 [switch]$decode)\r\n<\/code><\/pre>\n<h3>A note about regions in the ISE<\/h3>\n<p>One of the things I like about the Windows PowerShell ISE as it exists in Windows PowerShell\u00a04.0 is that it supports regions. (Actually, I think this was added in Windows PowerShell\u00a03.0.) This means I can add my own regions by using tags like:<\/p>\n<pre><code>\r\n#region getcontent\r\n#endregion\r\n<\/code><\/pre>\n<p>It is smart enough to create regions from common sections, such as the comment-based Help and the <strong>Parameter<\/strong> section. All I need to do is click the minus sign in the little square box on the left side of the code section, and it collapses the region. To open, I click the plus sign. This makes it easier to work with longer scripts. Here is what my script looks like with the Help and <b>Parameter<\/b> sections collapsed and the <b>CreateHashTables<\/b> region open:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-10-9-14-01.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-10-9-14-01.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<h3>The CreateHashTables region<\/h3>\n<p>The <b>CreateHashTables<\/b> portion of my script is basically the same code that I worked out earlier in the week. The only change I made is to add a couple of <b>Write-Verbose<\/b> commands. These do not display anything unless I run the function with the <b>\u2013Verbose<\/b> parameter. <b>\u2013Verbose<\/b> is one of the common parameters, and it is supported when I add in the <b>[cmdletbinding()]<\/b> attribute tag in my script. Because there are a lot of moving parts to this script now, I decided to add several <b>Write-Verbose<\/b> commands. Here is that section now, along with the collapsible region:<\/p>\n<pre><code>\r\n#region CreateHashTables\r\nWrite-Verbose \"Creating Hash Tables\"\r\n$asciiFirst = New-Object System.Collections.Hashtable\r\n$ltrFirst = New-Object System.Collections.Hashtable\r\n0..255 | Foreach-Object {\r\n    $asciiFirst.Add($_,([char]$_).ToString())\r\n}\r\nForEach ($k in $asciiFirst.Keys) {\r\n    $ltrFirst.add($asciiFirst[$k],$k)\r\n}\r\nWrite-Verbose \"Hash tables complete\"\r\n#endregion\r\n<\/code><\/pre>\n<h2>Get the content<\/h2>\n<p>One change I decided to make is that I basically support reading from and writing to a text file. The reason for this is that it was really impractical to paste in an array of ASCII numeric values. So I decided to use two text files. I used the same parameter names as other Windows PowerShell cmdlets use. So the input file is <b>\u2013Path<\/b>, and the output file is <b>\u2013FilePath<\/b>. This helps make the script easy to remember how to use.<\/p>\n<p>I first use a <b>Write-Verbose<\/b> cmdlet to state that I am reading input, and I pass the <b>$path<\/b> variable so I know exactly where I am reading. I then use the <b>Test-Path<\/b> cmdlet to ensure the file exists. If it does not, I return. I know it is common to use <b>Exit<\/b> here, but when I am running this in the Windows PowerShell ISE, it closes the Windows PowerShell ISE\u2014and that can be a real pain when writing and debugging a script. So I simply return, but do not return anything. Here is the script:<\/p>\n<pre><code>\r\n#region getcontent\r\nWrite-Verbose \"Reading input file from $path\"\r\nif (Test-path $path) {\r\n    $txtIN = Get-Content $path -Encoding Ascii\r\n} else {\r\n    \"Unable to find $path\"\r\n    return\r\n}\r\n#endregion\r\n<\/code><\/pre>\n<h2>Encode the content and write to file<\/h2>\n<p>Now I need to encode the content and write the ASCII numeric values to a text file if the script is called with the <b>\u2013Encode<\/b> parameter. The first thing I do is check to see if the <b>$encode<\/b> variable exists. If it does, the function was called with the <b>\u2013Encode<\/b> switched parameter.<\/p>\n<p>Next, I use <b>Write-Verbose<\/b> to display a message that states I am beginning the encode. The rest of the script is basically the same as I previously wrote. I then use the <b>Out-File<\/b> cmdlet to create an ASCII encoded text file. This script is shown here:<\/p>\n<pre><code>\r\n#region encode\r\nIf ($encode) {\r\n    Write-Verbose \"Encoding input file\"\r\n    $coded = $txtIN.ToCharArray() |\r\n        ForEach-Object { $ltrFirst[\"$_\"] }\r\n    Write-Verbose \"Encoding complete: `r`n $coded\"\r\n    Write-Verbose \"Writing to $filepath\"\r\n    Out-file -FilePath $filepath -inputobject $coded -Encoding ascii\r\n}\r\n#endregion\r\n<\/code><\/pre>\n<h2>Decode the file<\/h2>\n<p>The last thing to do is to decode the file. This region works the same as other portions of the script. One significant change I had to make was to cast the inputted value to an integer. This is because when the numbers are read from the text file via <b>Get-Content<\/b>, Windows PowerShell automatically cast the values to a string. So the lookups were not working. This actually took me nearly an hour to figure out (sometimes I am really slow like that). Here is the script:<\/p>\n<pre><code>\r\n#region decode\r\n    If($decode) {\r\n        Write-Verbose \"Decoding $path\"\r\n        $decoded = $txtIN |\r\n            ForEach-Object { $asciiFirst[[int32]$_] }\r\n        $decoded = $decoded -join ''\r\n        Write-Verbose \"Decoding complete: `r`n $decoded\"\r\n        Write-Verbose \"Writing to $filepath\"\r\n        $decoded | Out-file -FilePath $filepath -Encoding ascii\r\n    }\r\n#endregion\r\n} #end function Convert-AsciiEncoding\r\n<\/code><\/pre>\n<h2>Tests and limitations<\/h2>\n<p>I open the script file that contains the function in the Windows PowerShell ISE, and I click the green arrow to run the script. This loads the function into memory, but nothing is output to the output pane.<\/p>\n<p>The comment-based Help works great. I can call <b>Get-Help<\/b> and pass the <b>Convert-AsciiEncoding<\/b> function name and get basic Help. I can use <b>\u2013Examples<\/b> and get only examples, or <b>\u2013Full<\/b> and get the complete Help information. This is shown here:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-10-9-14-02.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-10-9-14-02.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>Now I create a simple text file with a single line in it. I do this in Notepad and save it in a location that I can easily find. I type the following string:<\/p>\n<p style=\"margin-left: 30px;\">\u201cThis is a simple text file; It has (1) number!\u201d<\/p>\n<p>Here is the content of the file:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-10-9-14-03.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-10-9-14-03.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>Now, I use the following command to encode the file:<\/p>\n<p style=\"margin-left: 30px;\">Convert-AsciiEncoding -path C:\\fso\\Simple.txt -filepath c:\\fso\\encodedSimple.txt -encode<\/p>\n<p>The encoded text file is shown here:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-10-9-14-04.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-10-9-14-04.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>Now I decode the file. I run it with the <b>\u2013Verbose<\/b> parameter. Here is my command:<\/p>\n<p style=\"margin-left: 30px;\">Convert-AsciiEncoding -path C:\\fso\\encodedSimple.txt -filepath c:\\fso\\decodedSimple.txt -decode -Verbose<\/p>\n<p>From the Windows PowerShell ISE output pane, I can see that my decode worked:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-10-9-14-05.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-10-9-14-05.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>And here is the text file that I decoded:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-10-9-14-06.png\"><img decoding=\"async\" title=\"Image of command output\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-10-9-14-06.png\" alt=\"Image of command output\" \/><\/a><\/p>\n<p>There is one annoying limitation with this function. It does not handle multiple-line text that I type into Notepad. For some reason, when I read-in the text file, it does not pick up that there is a Carriage Return (CR) character (ACSII 10) or a New Line (LF) character (ASCII 13). As a result, when I put in the decode text, the values are not in the outputted text file. When I use <b>Join<\/b> to put the text back together, there is nothing between the punctuation at the end of one line to the first character of a new line, so it all runs together.<\/p>\n<p>When I used a <b>Here-String<\/b> in my earlier blog posts, I was able to detect the CRLF sequence; and therefore, it worked. I suspect either something with Notepad, or something with the way <b>Get-Content<\/b> reads the file. But after messing around with for most of the afternoon, I decided to forgo it. If you figure it out, post a comment and share your wisdom.<\/p>\n<p>That is all there is to converting my encoding\/decoding script into a function. I hope you found this as much fun as I did. To save yourself a lot of typing, the complete script is available in the <del>Script Center Repository<\/del>.<\/p>\n<p>Join me tomorrow when I will talk about adding an offset capability and the ability to write to files.<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\" rel=\"noopener\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\" rel=\"noopener\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\" target=\"_blank\" rel=\"noopener\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\" rel=\"noopener\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><b>Ed Wilson, Microsoft Scripting Guy<\/b><span style=\"font-size: 12px;\">\u00a0<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Microsoft Scripting Guy, Ed Wilson, talks about converting a script to a Windows PowerShell function. Microsoft Scripting Guy, Ed Wilson, is here. When I have a script that I think I will use more than once or twice, it often makes sense for me to convert it to a function. If it is something [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[69,537,3,4,45],"class_list":["post-565","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-functions","tag-hash-tables","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Microsoft Scripting Guy, Ed Wilson, talks about converting a script to a Windows PowerShell function. Microsoft Scripting Guy, Ed Wilson, is here. When I have a script that I think I will use more than once or twice, it often makes sense for me to convert it to a function. If it is something [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/565","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=565"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/565\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=565"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=565"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=565"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}