{"id":50873,"date":"2010-03-25T00:01:00","date_gmt":"2010-03-25T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2010\/03\/25\/hey-scripting-guy-the-story-of-a-large-script-project\/"},"modified":"2010-03-25T00:01:00","modified_gmt":"2010-03-25T00:01:00","slug":"hey-scripting-guy-the-story-of-a-large-script-project","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-the-story-of-a-large-script-project\/","title":{"rendered":"Hey, Scripting Guy! The Story of a Large Script Project"},"content":{"rendered":"<p class=\"MsoNormal\"><b><span><font face=\"Calibri\"><a class=\"addthis_button\" href=\"http:\/\/www.addthis.com\/bookmark.php?v=250&amp;pub=scriptingguys\"><img decoding=\"async\" alt=\"Bookmark and Share\" src=\"http:\/\/s7.addthis.com\/static\/btn\/v2\/lg-share-en.gif\" width=\"125\" height=\"16\"><\/a><\/p>\n<p><\/font><\/span><\/b><\/p>\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\"><\/p>\n<p><\/font><\/span><\/b>&nbsp;<\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">Hello, everyone. This is <\/font><a href=\"http:\/\/blogs.technet.commailto:clinth@microsoft.com\"><font size=\"3\" face=\"Calibri\">Clint Huffman<\/font><\/a><font size=\"3\"><font face=\"Calibri\">, this time writing from Seattle, Washington, in the United States. I have hijacked this blog. Don&rsquo;t tell Ed. Just joking. Actually, Ed Wilson allowed me the honor of filling in for him on one of the blog posts and I hope he isn&rsquo;t disappointed. I&rsquo;ve been working on the <a href=\"http:\/\/pal.codeplex.com\/\">Performance Analysis of Logs (PAL)<\/a> tool for a few years now and have some war stories to tell. Weighing in at over 4,000 lines of code, the Performance Analysis of Logs (PAL) tool is the largest Windows PowerShell 2.0 script I have ever written. I had many challenges developing this tool and I hope my experiences can help you avoid some pitfalls when taking on a large script project.<\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><\/p>\n<p><\/font><\/font>&nbsp;<\/p>\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\">Why I wrote the PAL tool<\/p>\n<p><\/font><\/span><\/b><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Just like Indiana Jones&rsquo;s father who wrote everything he researched in his diary, I wrote everything I researched in my tool. Performance counters can tell us a <i>lot<\/i> about the behavior of a computer, but it is cumbersome to look at them all and still get a good return on the investment of your time. This was a great opportunity to introduce scripting automation to the Windows performance analysis world. <\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\">Is it a script or a tool?<\/p>\n<p><\/font><\/span><\/b><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">In my opinion, a script is something you write for yourself such as a script that shuts down unnecessary services for flight mode, or querying for the latest illegal MP3 files on your network. A &ldquo;tool&rdquo; is a script that you write for other people. You might think that is an easy transition, but ask any developer where they spend most of their time<span>&mdash;<\/span>handling user input. You have to expect the unexpected and provide a nice, easy-to-use interface. The PAL tool is a tool because I wrote it for other people to use.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\">Plan the trip<\/p>\n<p><\/font><\/span><\/b><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Just like planning a long trip, you don&rsquo;t just get in the car and go. You have to plan, pack, and give the kids a sedative. (<i>Scripting Editor<\/i>: Yikes.) Before I wrote any code for the PAL tool, I wrote a document that defines its specifications. The document specifies the goal of the project, such as what kind of work the user has to do and what will they get out of it; market analysis such as how does it compare to other products in the market; and the infrastructure needed to do the job.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\">Define the requirements<\/p>\n<p><\/font><\/span><\/b><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">The first version of PAL was written in VBScript and relied on a lot of technology that was free such as the ability to execute string values as inline code, Microsoft Log Parser, and Microsoft Office Web Components 2003 (OWC11). The free technologies made it easy to create line charts for the counters (a requirement for PAL), but both Log Parser and OWC11 are aging products that might not be around much longer. For PAL v2.0 to succeed, it needed a new and free technology to create charts.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\">The technology options<\/p>\n<p><\/font><\/span><\/b><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\"><font size=\"3\">I considered many different technologies for PAL v2.0. Here are a few of the technologies I seriously considered:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpFirst\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\"><font size=\"3\"><b>VBScript:<\/b> PAL v1.x was already written in VBScript, but needed a major overhaul. Like any impatient developer, when writing PAL v1.x I eventually didn&rsquo;t care about &ldquo;doing it the best way&rdquo; and cared more about just getting it working and out the door. My advice to you is to try to stick with doing it right. It helps in the long run.<\/p>\n<p>In any case, I was comfortable with VBScript, so I actually invested a lot of time writing PAL v2.0 in VBScript. I wrote VBScript classes and I was very happy with how the code was turning out, but VBScript is old technology (more than15 years old) and Windows PowerShell is the cool new scripting technology. Furthermore, I didn&rsquo;t have a replacement for Log Parser and OWC11.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\"><font size=\"3\"><b>VB.NET:<\/b> Many of my co-workers kept asking me, &ldquo;Why not just write the whole thing in .NET?&rdquo; While it sounds easy, there are challenges with this. The problem is that PAL needs to be able to run threshold code from an external XML file. For VB.NET to do this, it would have to compile the code during runtime to run it, and it would require more research on my part to get it to work.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\">\n<span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\"><font size=\"3\"><b>VB.NET Classes (assemblies):<\/b> I tried writing the core functionality of PAL in VB.NET class DLLs (assemblies), but I found that when I needed to update the assembly (DLL) I had to close the Windows PowerShell editor, recompile the assembly, and then bring the Windows PowerShell editor back up again. This became a painful process.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpLast\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\"><font face=\"Calibri\"><b>VB.NET Chart Controls:<\/b> Because I needed a new technology to create line charts and I had to keep it open source, I began writing my own .NET classes that generate line charts. This was a lot of fun, but I had to keep each chart between the values of 0 and 100 because the logic of creating a chart that autoscales was beyond me.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\"><br \/>The breakthroughs<\/p>\n<p><\/font><\/span><\/b><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">The development of PAL v2.0 stagnated because of the technology challenges of keeping it a free and open-source tool.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><b>MSChart Controls for .NET Framework v3.5:<\/b> The first breakthrough came with the release of the MSChart Controls. Microsoft released them as free .NET classes that we can use in .NET. VBScript is unable to use .NET classes, so this was a big push for me to give up on the VBScript development of PAL v2.0.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><b>Meeting Bruce Payette:<\/b> The second breakthrough was meeting Bruce Payette who is one of the development leads for Windows PowerShell. He led an introduction to PowerShell class for my team (Premier Field Engineering), and it was amazing. Every technology challenge I had with PAL, he was able to answer in Windows PowerShell. I was <i>finally<\/i> convinced that Windows PowerShell is the true path for PAL v2.0.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">Bruce&rsquo;s book, <i>PowerShell in Action<\/i>, was just right for me because it is designed for people who already know other programming languages. If you are new to programming, Ed Wilson&rsquo;s book, <\/font><a href=\"http:\/\/www.amazon.com\/Windows-PowerShell-2-0-Best-Practices\/dp\/0735626464\/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1255959455&amp;sr=8-1\"><font size=\"3\" face=\"Calibri\">Windows PowerShell 2.0 Best Practices<\/font><\/a><font size=\"3\"><font face=\"Calibri\">, is more appropriate.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">With the MSChart Controls freely available to everyone on the .NET v3.5 Framework, Bruce&rsquo;s assurances that Windows PowerShell can do the job, and the convenience of doing a pure Windows PowerShell solution, the path was clear. <\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\">Make it easy to read<\/p>\n<p><\/font><\/span><\/b><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Remember that a &ldquo;tool&rdquo; is a script that other people will use, so make it easy to read and follow the logic. Because Windows PowerShell must have the functions defined before you can execute them, I put all of my function calls at the bottom of the script. The flow of the script is easy to follow, so if anyone needs to debug it or needs to get an idea of what it does, they just look at the <b>Main() <\/b>function.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Below is a sample of what the PAL script <b>Main() <\/b>function looks like. Each line is a function call and I am using those function calls to group portions of the script together. Think of this as the traffic cop of the script:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span>InitializeGlobalVariables<br \/>ShowMainHeader<br \/>ProcessArgs<br \/>CreateSessionWorkingDirectory<br \/>Analyze<span>&nbsp;&nbsp;&nbsp;&nbsp; <\/span><br \/>PrepareDataForReport<br \/>GenerateHtml<br \/>SaveXmlReport<br \/>OpenHtmlReport<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">As you can see, the <b>Main()<\/b> function is the heart of the script, and I try to use easy-to-read function names, so the person reading the script should be able to follow along at least at a high level.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">The output needs to be concise and easy on the eyes. Here is a screenshot of the PAL tool executing.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><img decoding=\"async\" title=\"Image of PAL tool executing\" alt=\"Image of PAL tool executing\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2010\/march\/hey0325\/Pal_Processing.jpg\" width=\"600\" height=\"386\"><\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\"><br \/>The problem is &ldquo;choice&rdquo; -eq a VBScript Legacy<\/p>\n<p><\/font><\/span><\/b><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Just like in &ldquo;The Matrix&rdquo; trilogy, the problem is &ldquo;choice.&rdquo; One of the challenges I had when writing Windows PowerShell scripts from a VBScript background is the expression evaluation of the <b>If<\/b> statements&mdash;meaning I kept using equal signs (<b>=<\/b>) where I should have been using <b>-eq<\/b> statements. For example, I would write the following <b>If<\/b> statement and it would *always* evaluate to True:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span>If ($a = 1) {# Do Something}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">I come from a VB\nScript background, so to me this looks perfectly okay, but what is happening is the equal sign (<b>=<\/b>) is assigning the value of 1 to <b>$a<\/b> instead of evaluating it. After hitting this problem about 30 or so times, I am getting better at it. The correct statement should look like this:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span>If ($a &ndash;eq 1) {# Do Something}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Where the <b>-eq<\/b> evaluates the variable <b>$a<\/b> to see if it equals 1.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\">Debugging with Windows PowerShell ISE versus PowerGUI<\/p>\n<p><\/font><\/span><\/b><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">I came from a VBScript background, so it was challenging to develop a new application in Windows PowerShell. My crutch for a lot of my work was PowerGUI because it helped out a lot with my <b>If&#8230;Then<\/b> statements like above. All I had to do was press Ctrl+B, type the VBScript I am trying to do such as <b>If<\/b>, and it would create the Windows PowerShell equivalent code.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">While I really like PowerGUI, it had problems with debugging a 5,000-line script such as PAL. I&rsquo;ve heard that the scalability problem has been fixed as of the writing of this document, but at the time, it took too long to be usable. Therefore, I used PowerGUI to write my Windows PowerShell functions, and then copied the code to the main script and debugged the main script in Windows PowerShell ISE, which ran very fast when debugging.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">Windows PowerShell ISE ships with Windows PowerShell v2.0. PowerGUI is a free download from Quest Software at <\/font><a href=\"http:\/\/www.powergui.org\/\"><font size=\"3\" face=\"Calibri\">http:\/\/www.powergui.org<\/font><\/a><font size=\"3\"><font face=\"Calibri\">. <\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\"><br \/>Going primitive<\/p>\n<p><\/font><\/span><\/b><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Early on in the development of PAL v2.0 in Windows PowerShell, I noticed that its performance is a lot slower than PAL v1.x. I discovered that the slowest part was using built-in cmdlets such <b>Import-CSV<\/b>. I was using <b>Import-CSV<\/b> to read in the performance counter log (I use relog.exe to convert the binary log from BLG to text [CSV]), I would extract the counter data from it. It looked something like this:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span>$oCsvCounterLog = Import-CSV &ndash;path &lsquo;.counterlog.csv&rsquo;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">The problem is that this line would cause the memory consumption on the computer to be in the gigabytes. This is because the performance counter logs can become very large. Colleagues on my team suggested piping it like this:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span>$counter = Import-CSV &ndash;Path &lsquo;.counterlog.csv&rsquo; | ForEach-Object {$_.&#8221;\\DemoComputerProcessor(_Total)% Processor Time&#8221;}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">This solved the memory bloat problem, but every time this line was called, it would take several seconds to process. When dealing with thousands of counters, this was unacceptably slow. Therefore, I had to bite the bullet and go primitive.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">What I mean be &ldquo;going primitive&rdquo; is that instead of using the built-in cmdlets in Windows PowerShell (in this case the <b>Import-CSV<\/b> cmdlet), I simply read the CSV file into memory as a two-dimensional array. The code is a bit too complex to post in this document, but I hope you get the idea.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">The point is that, if the built-in cmdlets are too slow for you, consider parsing the data using more primitive methods.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\"><br \/>&ldquo;I&rdquo; before &ldquo;e&rdquo; except after &ldquo;c&rdquo;<\/p>\n<p><\/font><\/span><\/b><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Another concept I had to learn was the order of functions. In VBScript, you can place your functions anywhere in the script and it would work. In Windows PowerShell (and other languages like C), the functions must be defined before you can use them. Typically, in my VBScript I would have the <b>Main()<\/b> function at the top of the script, so the human looking at the code need only scroll down a little bit to see the flow of the script. In Windows PowerShell, I had to reverse this&mdash;meaning I put the <b>Main()<\/b> function at the bottom of the script, so that all of the functions that are called are already defined before the <b>Main() <\/b>function is called.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\"><br \/>Conclusion<\/p>\n<p><\/font><\/span><\/b><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">There are a lot of great technologies out there that I could have used to create PAL v2.0, but a pure Windows PowerShell solution worked best for me. With that said, I had a lot of growing pains using Windows PowerShell and I hope that you can avoid some of the pitfalls that I ran into.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Oh, and here is one of the cool charts that is generated by the PAL tool: <\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><img decoding=\"async\" title=\"Image of chart generated by PAL tool\" alt=\"Image of chart generated by PAL tool\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2010\/march\/hey0325\/memory_available_mbytes.jpg\" width=\"600\" height=\"450\"><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><\/p>\n<p><\/font><\/font>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>&nbsp; Hello, everyone. This is Clint Huffman, this time writing from Seattle, Washington, in the United States. I have hijacked this blog. Don&rsquo;t tell Ed. Just joking. Actually, Ed Wilson allowed me the honor of filling in for him on one of the blog posts and I hope he isn&rsquo;t disappointed. I&rsquo;ve been working on [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[76,56,3,4,45,77],"class_list":["post-50873","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-clint-huffman","tag-guest-blogger","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell","tag-writing"],"acf":[],"blog_post_summary":"<p>&nbsp; Hello, everyone. This is Clint Huffman, this time writing from Seattle, Washington, in the United States. I have hijacked this blog. Don&rsquo;t tell Ed. Just joking. Actually, Ed Wilson allowed me the honor of filling in for him on one of the blog posts and I hope he isn&rsquo;t disappointed. I&rsquo;ve been working on [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/50873","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\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=50873"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/50873\/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=50873"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=50873"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=50873"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}