{"id":168,"date":"2012-02-16T11:16:19","date_gmt":"2012-02-16T18:16:19","guid":{"rendered":"http:\/\/ryan.witschger.net\/?p=168"},"modified":"2012-02-16T11:16:19","modified_gmt":"2012-02-16T18:16:19","slug":"dynamic-write-progress-percentages","status":"publish","type":"post","link":"http:\/\/www.Get-Blog.com\/?p=168","title":{"rendered":"Dynamic Write-Progress Percentages"},"content":{"rendered":"<p>I have had problems in my very long scripts with Write-Progress.  When writing the script you add in several Write-Progress statements and give them a percentage, the problem is that the percentage you give it is likely a number that you are typing in and hard coding.  This is a problem because if you add to the code or move the code around you have to go back and change every one of these items.<\/p>\n<p>In a recent script I wrote that was just over 300 lines I had to repeat this process 5 times which was really frustrating, so I thought up a way to make sure I don&#8217;t have to do it again.<\/p>\n<p>Now, I should state beforehand that this process only works if each write-progress is executed once and only once.  Any changes to that means you may need to add some other intelligence, but in general I think this is a good starting point.<\/p>\n<p>The first thing you want to do is read the script file that you are executing to get a count of the number of lines with a &#8220;Write-Progress&#8221; in them.  Then store a tracking number as a global variable so that all functions can see it. Do this at the start of the script.<\/p>\n<p>Here is my single line to do all of the above:<\/p>\n<div class=\"codecolorer-container powershell solarized-dark\" style=\"overflow:auto;white-space:nowrap;width:680px;\"><div class=\"powershell codecolorer\"><span class=\"re0\">$ProgressIndicators<\/span> <span class=\"sy0\">=<\/span> $<span class=\"br0\">&#40;<\/span><span class=\"re0\">$Global<\/span>:I<span class=\"sy0\">=<\/span><span class=\"nu0\">0<\/span>; $<span class=\"br0\">&#40;<\/span><span class=\"kw2\">GC<\/span> <span class=\"re0\">$MyInvocation<\/span>.InvocationName <span class=\"sy0\">|<\/span> <span class=\"sy0\">?<\/span> <span class=\"br0\">&#123;<\/span><a href=\"about:blank\"><span class=\"kw6\">$_<\/span><\/a> <span class=\"kw4\">-Match<\/span> <span class=\"st0\">&quot;Write-Progress&quot;<\/span><span class=\"br0\">&#125;<\/span><span class=\"br0\">&#41;<\/span>.count <span class=\"sy0\">-<\/span><span class=\"nu0\">1<\/span><span class=\"br0\">&#41;<\/span><\/div><\/div>\n<p>So, there is a whole lot of learning packed into a small area here.  Let me cover the confusing sections.  First naming a variable <code class=\"codecolorer powershell default\"><span class=\"powershell\"><span class=\"re0\">$Global<\/span>:I<\/span><\/code> will create a global variable called &#8220;I&#8221; that is accessible from all PowerShell scopes.  As well, there is a &#8220;;&#8221; after this portion to execute this as a separate line of code.  Since there is no output, this will not become part of the variable when run like this.  For more information on scopes visit <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd315289.aspx\">http:\/\/technet.microsoft.com\/en-us\/library\/dd315289.aspx<\/a>.<\/p>\n<p>The command <code class=\"codecolorer powershell default\"><span class=\"powershell\"><span class=\"kw2\">GC<\/span><\/span><\/code> is a built-in alias for <code class=\"codecolorer powershell default\"><span class=\"powershell\"><span class=\"kw1\">Get-Content<\/span><\/span><\/code>.<\/p>\n<p>The object property &#8220;<code class=\"codecolorer powershell default\"><span class=\"powershell\"><span class=\"re0\">$MyInvocation<\/span>.InvocationName<\/span><\/code> will provide the file path of the script that is currently being executed.  So, in effect, we are reading the entire script that is currently running.<\/p>\n<p>Then we pipe the script contents to <code class=\"codecolorer powershell default\"><span class=\"powershell\"><span class=\"sy0\">?<\/span><\/span><\/code> which is a built-in alias for <code class=\"codecolorer powershell default\"><span class=\"powershell\"><span class=\"kw1\">Where-Object<\/span><\/span><\/code>.  Then we look for a match on &#8220;Write-Progress&#8221;.  You&#8217;ll notice that this whole section is surrounded in <code class=\"codecolorer powershell default\"><span class=\"powershell\">$<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><\/span><\/code> which &#8220;Instantiates&#8221; a variable for this this output.  Then we can find the number of lines with the .count property.  After that, I subtract 1 so it doesn&#8217;t count itself.<\/p>\n<p>All of this means that the variable <code class=\"codecolorer powershell default\"><span class=\"powershell\">$ProgressIndicators<\/span><\/code> will now contain the number of times &#8220;Write-Progress&#8221; appears in the script, no matter what script you place it in.<\/p>\n<p>So now all we need is for <code class=\"codecolorer powershell default\"><span class=\"powershell\"><span class=\"kw1\">Write-Progress<\/span><\/span><\/code> to read these two values when it run and increment our global tracking variable.<\/p>\n<div class=\"codecolorer-container powershell solarized-dark\" style=\"overflow:auto;white-space:nowrap;width:680px;\"><div class=\"powershell codecolorer\"><span class=\"kw1\">Write-Progress<\/span> <span class=\"kw5\">-Activity<\/span> <span class=\"st0\">&quot;Making Progress&quot;<\/span> <span class=\"kw5\">-Status<\/span> <span class=\"st0\">&quot;Test 1&quot;<\/span> <span class=\"kw5\">-PercentComplete<\/span> $<span class=\"br0\">&#40;<\/span>$<span class=\"br0\">&#40;<\/span><span class=\"re0\">$Global<\/span>:I<span class=\"sy0\">++<\/span>;$Global:I<span class=\"br0\">&#41;<\/span> <span class=\"sy0\">\/<\/span> <span class=\"re0\">$ProgressIndicators<\/span> <span class=\"sy0\">*<\/span> <span class=\"nu0\">100<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"kw2\">Sleep<\/span> <span class=\"nu0\">1<\/span><br \/>\n<span class=\"kw1\">Write-Progress<\/span> <span class=\"kw5\">-Activity<\/span> <span class=\"st0\">&quot;Making Progress&quot;<\/span> <span class=\"kw5\">-Status<\/span> <span class=\"st0\">&quot;Test 2&quot;<\/span> <span class=\"kw5\">-PercentComplete<\/span> $<span class=\"br0\">&#40;<\/span>$<span class=\"br0\">&#40;<\/span><span class=\"re0\">$Global<\/span>:I<span class=\"sy0\">++<\/span>;$Global:I<span class=\"br0\">&#41;<\/span> <span class=\"sy0\">\/<\/span> <span class=\"re0\">$ProgressIndicators<\/span> <span class=\"sy0\">*<\/span> <span class=\"nu0\">100<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"kw2\">Sleep<\/span> <span class=\"nu0\">1<\/span><br \/>\n<span class=\"kw1\">Write-Progress<\/span> <span class=\"kw5\">-Activity<\/span> <span class=\"st0\">&quot;Making Progress&quot;<\/span> <span class=\"kw5\">-Status<\/span> <span class=\"st0\">&quot;Test 3&quot;<\/span> <span class=\"kw5\">-PercentComplete<\/span> $<span class=\"br0\">&#40;<\/span>$<span class=\"br0\">&#40;<\/span><span class=\"re0\">$Global<\/span>:I<span class=\"sy0\">++<\/span>;$Global:I<span class=\"br0\">&#41;<\/span> <span class=\"sy0\">\/<\/span> <span class=\"re0\">$ProgressIndicators<\/span> <span class=\"sy0\">*<\/span> <span class=\"nu0\">100<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"kw2\">Sleep<\/span> <span class=\"nu0\">1<\/span><\/div><\/div>\n<p>So, as you can see I use the same techniques described above to increment the variable and then do some basic match to get a percentage of total progress.<\/p>\n<p>You can throw this code into any script to give you a dynamically updating progress indicator.  There are two things to keep in mind.  If you have a loop that runs a <code class=\"codecolorer powershell default\"><span class=\"powershell\"><span class=\"kw1\">write-progress<\/span><\/span><\/code> command over and over then it will continue to increment and break this.  You could remove the <code class=\"codecolorer powershell default\"><span class=\"powershell\"><span class=\"re0\">$Global<\/span>:I<span class=\"sy0\">++<\/span><\/span><\/code> portion or make a sub progress bar to correctly display the progress of the loop.  Also, you might skip a <code class=\"codecolorer powershell default\"><span class=\"powershell\"><span class=\"kw1\">write-progress<\/span><\/span><\/code> line due to an <code class=\"codecolorer powershell default\"><span class=\"powershell\"><span class=\"kw3\">IF<\/span><\/span><\/code> statement.  If so maybe you could increment the value of <code class=\"codecolorer powershell default\"><span class=\"powershell\"><span class=\"re0\">$Global<\/span>:I<\/span><\/code> in an <code class=\"codecolorer powershell default\"><span class=\"powershell\"><span class=\"kw3\">ELSE<\/span><\/span><\/code> statement.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I have had problems in my very long scripts with Write-Progress. When writing the script you add in several Write-Progress statements and give them a percentage, the problem is that the percentage you give it is likely a number that you are typing in and hard coding. This is a problem because if you add <a href='http:\/\/www.Get-Blog.com\/?p=168' class='excerpt-more'>[&#8230;]<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[4],"tags":[],"_links":{"self":[{"href":"http:\/\/www.Get-Blog.com\/index.php?rest_route=\/wp\/v2\/posts\/168"}],"collection":[{"href":"http:\/\/www.Get-Blog.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.Get-Blog.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.Get-Blog.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.Get-Blog.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=168"}],"version-history":[{"count":0,"href":"http:\/\/www.Get-Blog.com\/index.php?rest_route=\/wp\/v2\/posts\/168\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.Get-Blog.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=168"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.Get-Blog.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=168"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.Get-Blog.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=168"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}