{"id":6700,"date":"2019-04-24T08:50:49","date_gmt":"2019-04-24T12:50:49","guid":{"rendered":"https:\/\/jdhitsolutions.com\/blog\/?p=6700"},"modified":"2021-04-09T14:50:20","modified_gmt":"2021-04-09T18:50:20","slug":"more-powershell-adventures-in-cleaning-your-path","status":"publish","type":"post","link":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/","title":{"rendered":"More PowerShell Adventures in Cleaning Your Path"},"content":{"rendered":"\n<p>A few days ago, <a title=\"review the previous article\" href=\"https:\/\/jdhitsolutions.com\/blog\/?p=6672\" target=\"_blank\" rel=\"blank noopener noreferrer\">I posted an article<\/a> that demonstrated a number of PowerShell techniques and concepts that you could use to clear out obsolete locations in your %PATH% environment variable. For those of you new to my blog I want to make sure you understand that I often use a scenario, such as this one, to demonstrate and teach PowerShell. Don't assume that any functions or code samples are complete and production-ready. \\After posting, I heard from a number of people suggesting the SetEnvironmentVariable() method from the .NET Framework - especially because I used the framework to discover the path splitter. So let's go back to the problem at hand and explore this approach.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Cmdlets First<\/h2>\n\n\n\n<p>I have a PowerShell scripting philosophy that I espouse, especially for beginners. If there is a cmdlet you can you to achieve a task, use it over the .NET Framework. By this I mean, use the <a title=\"Read online help for this command\" href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113313\" target=\"_blank\" rel=\"noopener noreferrer\">Get-Date<\/a> cmdlet to get the current date and time and not [datetime]::Now. There are a few reasons.<\/p>\n\n\n\n<p>First, I don't want PowerShell beginners to feel the need to be .NET developers to write scripts and functions. The whole point of PowerShell is that there is a ton of abstraction so that scripters <em>do not<\/em> have to know the .NET Framework.&nbsp; Cmdlets are easier to discover. They are documented. And most importantly in the scenario we are using, should support -WhatIf. If you run a .NET method, there is no built-in safety. It runs. However, you can add support for -WhatIf which I'll get to in a bit.<\/p>\n\n\n\n<p>Yes, at some point you may turn to the .NET Framework for performance gains or accomplish something where there is no cmdlet. But this is after you have gained some experience. And don't forget that you can't Pester test the .NET Framework directly. There is no way to mock [Environment]::SetEnvironmentVariable(). There's nothing wrong with jumping to the .NET Framework. I just think you need a very good reason and make sure you add plenty of code documentation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Exploring a .NET Class<\/h2>\n\n\n\n<p>With all this in mind, let me step through the process of building some PowerShell tooling using the [system.environment] class. We will assume that you read about somewhere or saw it in a Stack Overflow question. You can, and probably should do a search for system.enviroment which should get you to a Microsoft documentation page. Or you can try exploring it in PowerShell. At a prompt type [System.Environment]:: and press Space.&nbsp; You can cycle through static properties and methods. Or if you have the PSReadline module loads, press Ctrl+Space<\/p>\n\n\n\n<figure class=\"wp-block-image is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image-18.png\"><img decoding=\"async\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-18.png\" alt=\"Exploring .NET members\" title=\"Exploring .NET members\"\/><\/a><\/figure>\n\n\n\n<p>By the way, technically you don't need to include the System prefix. If you just type [Environment]:: PowerShell knows you mean System.Environment.<\/p>\n\n\n\n<p>Looking at the list there is something called GetEnvironmentVariable. PowerShell may insert an open parenthesis which is your clue that this is a method. If you are using PSReadline, you can see the method definition. Or just press Enter without ().<\/p>\n\n\n\n<figure class=\"wp-block-image is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image-19.png\"><img decoding=\"async\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-19.png\" alt=\"The GetEnvironmentalVariable method\" title=\"The GetEnvironmentalVariable method\"\/><\/a><\/figure>\n\n\n\n<p>The output indicates there are two ways to use this method, each resulting in a string.&nbsp; The first takes the name of a variable. That seems simple enough to test.<\/p>\n\n\n\n<figure class=\"wp-block-image is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image-20.png\"><img decoding=\"async\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-20.png\" alt=\"Getting an environment variable\" title=\"Getting an environment variable\"\/><\/a><\/figure>\n\n\n\n<p>The second method takes a 2nd parameter. If you see a type that isn't something typical like string, int or boolean, there's a good chance it is an enumeration. You can use the same technique to discover possible values.<\/p>\n\n\n\n<figure class=\"wp-block-image is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image-21.png\"><img decoding=\"async\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-21.png\" alt=\"Target values\" title=\"Target values\"\/><\/a><\/figure>\n\n\n\n<p>And that makes sense because you can have User or System environment variables. Let's try.<\/p>\n\n\n\n<figure class=\"wp-block-image is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image-22.png\"><img decoding=\"async\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-22.png\" alt=\"Getting User and System environment variable\" title=\"Getting User and System environment variable\"\/><\/a><\/figure>\n\n\n\n<p>I didn't need to specifically cast \"User\" or \"Machine\". Because it is the second parameter, PowerShell knows what it should be.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Testing %PATH%<\/h2>\n\n\n\n<p>With this in mind let's turn to validating paths. Here a few commands that will be the basis of function.<\/p>\n\n\n\n<pre class=\"wp-block-code lang:ps mark:0 decode:true\"><code lang=\"powershell\" class=\"language-powershell\">[System.Environment]::GetEnvironmentVariable(\"PATH\", \"User\") -split \";\" | where-object { $_ -AND -Not (Test-Path $_) }\n[System.Environment]::GetEnvironmentVariable(\"PATH\", \"Machine\") -split \";\" | where-object {$_ -AND -Not (Test-Path $_) }\n<\/code><\/pre>\n\n\n\n<p>My <a title=\"Read online help for this command\" href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113423\" target=\"_blank\" rel=\"noopener noreferrer\">Where-Object<\/a> statement is also filtering out blanks which might happen if the variable ends with a semi-colon.<\/p>\n\n\n\n<figure class=\"wp-block-image is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image-23.png\"><img decoding=\"async\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-23.png\" alt=\"My bad paths\" title=\"My bad paths\"\/><\/a><\/figure>\n\n\n\n<p>I could just focus on string values - like paths that don't exist - but I always like to think about rich objects in the pipeline. Things that I might consume and re-use in ways I may have not even thought about yet. With that in mind, I wrote this function.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">Function Get-EnvPath {\n    [cmdletbinding()]\n    [OutputType(\"myEnvPath\")]\n    Param(\n        [ValidateSet(\"All\",\"User\",\"System\")]\n        [string]$Scope = \"All\"\n    )\n\n    #get the path separator character specific to this operating system\n    $splitter = [System.IO.Path]::PathSeparator\n\n    if ($scope -ne \"System\") {\n\n        Write-Verbose \"Validating USER paths\"\n\n        #filter out blanks if path ends in a splitter\n        [System.Environment]::GetEnvironmentVariable(\"PATH\", \"User\") -split $splitter |\n        Where-Object { $_ } | Foreach-Object {\n            # create a custom object based on each path\n            Write-Verbose \"  $_\"\n            [pscustomobject]@{\n                PSTypeName   = \"myEnvPath\"\n                Scope = \"User\"\n                Computername = [System.Environment]::MachineName\n                UserName     = [System.Environment]::UserName\n                Target       = \"User\"\n                Path         = $_\n                Exists       = Test-Path $_\n            }\n        } #foreach\n    }\n    if ($Scope -ne \"User\") {\n        Write-Verbose \"Validating MACHINE paths\"\n        [System.Environment]::GetEnvironmentVariable(\"PATH\", \"Machine\") -split $splitter |\n        Where-Object { $_ } | Foreach-Object {\n            # create a custom object based on each path\n            Write-Verbose \"  $_\"\n            [pscustomobject]@{\n                PSTypeName   = \"myEnvPath\"\n                Scope        = \"System\"\n                Computername = [System.Environment]::MachineName\n                UserName     = [System.Environment]::UserName\n                Target       = \"Machine\"\n                Path         = $_\n                Exists       = Test-Path $_\n            }\n        } #foreach\n    }\n} #end function<\/code><\/pre>\n\n\n\n<p>I debated on the appropriate verb. In the previous article, I used the Test verb. But the more I thought about it I realized what I was really doing was getting a bunch of objects that reflected that state of each location in %PATH%. An argument could be made that the <a title=\"Read online help for this command\" href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113418\" target=\"_blank\" rel=\"noopener noreferrer\">Test-Path<\/a> functionality should be handled separately, but since I know this is the main reason I'm writing this function I don't mind including it here. Now I have an easy to use function.<\/p>\n\n\n\n<figure class=\"wp-block-image is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image-24.png\"><img decoding=\"async\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-24.png\" alt=\"Finding obsolete paths with my PowerShell function\" title=\"Finding obsolete paths with my PowerShell function\"\/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Remediating the %PATH% Variable<\/h2>\n\n\n\n<p>The second half of the challenge to remove the bad locations. This is where I can use the SetEnvironmentVariable() method. If you use the techniques I showed, you'll discover that you need to provide the name of the variable, the new path, and the target. Something like this:<\/p>\n\n\n\n<pre class=\"wp-block-code lang:ps mark:0 decode:true\"><code lang=\"powershell\" class=\"language-powershell\">[System.Environment]::SetEnvironmentVariable(\"PATH\",$revised,\"User\")\n<\/code><\/pre>\n\n\n\n<p>However, as I mentioned there's no support for -WhatIf or other safety checks. If I run that command PowerShell will happily do it. I want to keep that in mind. I also want to create a function that can consume objects from the pipeline. Let me show you what I came up with and then we'll discuss.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">Function Repair-EnvPath {\n     [cmdletbinding(SupportsShouldProcess)]\n     Param(\n         [Parameter(Position = 0, Mandatory, ValueFromPipelineByPropertyName)]\n         [string]$Path,\n         [Parameter(Mandatory, ValueFromPipelineByPropertyName)]\n         [ValidateSet(\"User\",\"Machine\")]\n         [string]$Target\n     )\n    Begin {\n        Write-Verbose \"Starting $($myinvocation.MyCommand)\"\n        #get the path separator character specific to this operating system\n        $splitter = [System.IO.Path]::PathSeparator\n    }\n\n    Process {\n        Write-Verbose \"Removing $path from Target %PATH% setting\"\n        #get current values as an array\n        $paths = [system.environment]::GetEnvironmentVariable(\"PATH\", $Target) -split $splitter\n        $Corrected = (($paths).where( { $_ -ne $Path })) -join $splitter\n        Write-Verbose \"Setting a new value of $corrected\"\n        #add code support for -WhatIf\n        if ($PSCmdlet.ShouldProcess(\"$Target %Path% variable\", \"Remove $Path\")) {\n            [System.Environment]::SetEnvironmentVariable(\"PATH\", $Corrected, $Target)\n        }\n    }\n    End {\n        Write-Verbose \"Ending $($myinvocation.MyCommand)\"\n    }\n\n }<\/code><\/pre>\n\n\n\n<p>Again, I tried to choose a meaningful verb. An argument could be made that it should be Set-EnvPath to go along with Get-EnvPath. Or maybe Update-EnvPath. To me, I am using this command to repair a problem.<\/p>\n\n\n\n<p>The first thing to notice is in [cmdletbinding()]. I am telling PowerShell to include support for -WhatIf automatically in this function. I can run Repair-EnvPath -Whatif and the ShouldProcess directive will be passed to any cmdlets in the function that also supports -WhatIf. However, the majority of code in the function is native .NET. I'll have to handle it myself.<\/p>\n\n\n\n<p>The function takes two mandatory parameters, a path string, and environment target.&nbsp; I've gone ahead and included a validation test on the latter. This means the user can only enter User or Machine. As an added bonus, PowerShell will present those as tab-complete options.<\/p>\n\n\n\n<p>Notice that the parameter names are identical to some of the properties of the Get-EnvPath function. Because I've told PowerShell use bind those parameters by property name when the function sees an object with a property of Path it will use that value. The same goes for Target.<\/p>\n\n\n\n<p>The Process scriptblock is getting all of the paths from the given target and splitting them into an array.<\/p>\n\n\n\n<pre class=\"wp-block-code lang:ps mark:0 decode:true\"><code lang=\"powershell\" class=\"language-powershell\">$paths = [system.environment]::GetEnvironmentVariable(\"PATH\", $Target) -split $splitter\n<\/code><\/pre>\n\n\n\n<p>Then I can build a new string of paths that don't include the \"bad\" path.<\/p>\n\n\n\n<pre class=\"wp-block-code lang:ps mark:0 decode:true\"><code lang=\"powershell\" class=\"language-powershell\">$Corrected = (($paths).where( { $_ -ne $Path })) -join $splitter\n<\/code><\/pre>\n\n\n\n<p>Now for the final step. I need to invoke the SetEnvironmentVariable method, but I need to add support for -WhatIf.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Write Your Own WhatIf<\/h2>\n\n\n\n<p>When writing a PowerShell script or function, you have a built-in object referenced by $PSCmdlet. This object has a method called ShouldProcess(). To use, create an IF statement.&nbsp; The code in the&nbsp; IF block will run if -Whatif is <strong><em>not<\/em><\/strong> detected.<\/p>\n\n\n\n<pre class=\"wp-block-code lang:ps mark:0 decode:true\"><code lang=\"powershell\" class=\"language-powershell\">If ($PSCmdlet.ShouldProcess(\"target\")) {\n    #do something serious\n}\n<\/code><\/pre>\n\n\n\n<p>If PowerShell detects that the person running your script used -WhatIf, then PowerShell will use the parameters in the ShouldProcess method to create the message you see: \"What if: Performing the operation &lt;your command&gt; on target &lt;your target&gt;:\" So typically all you need to do is provide a value for the target such as a path, name or whatever you are modifying. However, you can take a&nbsp; bit more control as there are a few other parameters options.<\/p>\n\n\n\n<figure class=\"wp-block-image is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image-25.png\"><img decoding=\"async\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-25.png\" alt=\"ShouldProcess parameters\" title=\"ShouldProcess parameters\"\/><\/a><\/figure>\n\n\n\n<p>I often will also define the action. That's what I am doing in my code.<\/p>\n\n\n\n<pre class=\"wp-block-code lang:ps mark:0 decode:true\"><code lang=\"powershell\" class=\"language-powershell\">if ($PSCmdlet.ShouldProcess(\"$Target %Path% variable\", \"Remove $Path\")) {\n  [System.Environment]::SetEnvironmentVariable(\"PATH\", $Corrected, $Target)\n}\n<\/code><\/pre>\n\n\n\n<p>As you can see, you can be creative with the target and action values.<\/p>\n\n\n\n<figure class=\"wp-block-image is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image-26.png\"><img decoding=\"async\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-26.png\" alt=\"My custom WhatIf message\" title=\"My custom WhatIf message\"\/><\/a><\/figure>\n\n\n\n<p>Don't forget that you need the SupportsShouldProcess directive in [cmdletbinding()] AND the If statement. By the way, you can have as many WhatIf statements as you need in your code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cleaning Up My Paths<\/h2>\n\n\n\n<p>With all that in mind, here's how I can use my commands.<\/p>\n\n\n\n<figure class=\"wp-block-image is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image-27.png\"><img decoding=\"async\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-27.png\" alt=\"Verifying Removals\" title=\"Verifying Removals\"\/><\/a><\/figure>\n\n\n\n<p>Notice that they were written to work together. I might consider modifying the Get-EnvPath function to include a filtering option for Target as well as if the path exists. When creating PowerShell tools you need to think about who will use it, how, and their expectations. Since I am most likely to use this tool to clean up obsolete paths, it might be nice to include a filtering option so I don't have to remember to use the intermediate Where-Object expression. But I'll leave that to you if you are interested. Otherwise, time to clean up.<\/p>\n\n\n\n<figure class=\"wp-block-image is-style-default\"><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image-28.png\"><img decoding=\"async\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-28.png\" alt=\"Cleaning %PATH% with my new PowerShell functions\" title=\"Cleaning %PATH% with my new PowerShell functions\"\/><\/a><\/figure>\n\n\n\n<p>You can probably even think of some enhancements to Repair-EnvPath such as providing feedback about what is getting deleted or adding a -Passthru parameter. Again, I'll leave that fun for you.<\/p>\n\n\n\n<p>I still prefer to use cmdlets and native PowerShell wherever possible, but the .NET Framework can fill in the gaps. It is up to you to use it responsibly.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A few days ago, I posted an article that demonstrated a number of PowerShell techniques and concepts that you could use to clear out obsolete locations in your %PATH% environment variable. For those of you new to my blog I want to make sure you understand that I often use a scenario, such as this&#8230;<\/p>\n","protected":false},"author":1,"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,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"New on the blog: More #PowerShell Adventures in Cleaning Your Path","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[4,8],"tags":[184,32,534],"class_list":["post-6700","post","type-post","status-publish","format-standard","hentry","category-powershell","category-scripting","tag-net","tag-functions","tag-powershell"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>More PowerShell Adventures in Cleaning Your Path &#8226; The Lonely Administrator<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"More PowerShell Adventures in Cleaning Your Path &#8226; The Lonely Administrator\" \/>\n<meta property=\"og:description\" content=\"A few days ago, I posted an article that demonstrated a number of PowerShell techniques and concepts that you could use to clear out obsolete locations in your %PATH% environment variable. For those of you new to my blog I want to make sure you understand that I often use a scenario, such as this...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/\" \/>\n<meta property=\"og:site_name\" content=\"The Lonely Administrator\" \/>\n<meta property=\"article:published_time\" content=\"2019-04-24T12:50:49+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2021-04-09T18:50:20+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-18.png\" \/>\n<meta name=\"author\" content=\"Jeffery Hicks\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@JeffHicks\" \/>\n<meta name=\"twitter:site\" content=\"@JeffHicks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jeffery Hicks\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6700\\\/more-powershell-adventures-in-cleaning-your-path\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6700\\\/more-powershell-adventures-in-cleaning-your-path\\\/\"},\"author\":{\"name\":\"Jeffery Hicks\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\"},\"headline\":\"More PowerShell Adventures in Cleaning Your Path\",\"datePublished\":\"2019-04-24T12:50:49+00:00\",\"dateModified\":\"2021-04-09T18:50:20+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6700\\\/more-powershell-adventures-in-cleaning-your-path\\\/\"},\"wordCount\":1532,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\"},\"image\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6700\\\/more-powershell-adventures-in-cleaning-your-path\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/04\\\/image_thumb-18.png\",\"keywords\":[\".NET\",\"functions\",\"PowerShell\"],\"articleSection\":[\"PowerShell\",\"Scripting\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6700\\\/more-powershell-adventures-in-cleaning-your-path\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6700\\\/more-powershell-adventures-in-cleaning-your-path\\\/\",\"url\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6700\\\/more-powershell-adventures-in-cleaning-your-path\\\/\",\"name\":\"More PowerShell Adventures in Cleaning Your Path &#8226; The Lonely Administrator\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6700\\\/more-powershell-adventures-in-cleaning-your-path\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6700\\\/more-powershell-adventures-in-cleaning-your-path\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/04\\\/image_thumb-18.png\",\"datePublished\":\"2019-04-24T12:50:49+00:00\",\"dateModified\":\"2021-04-09T18:50:20+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6700\\\/more-powershell-adventures-in-cleaning-your-path\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6700\\\/more-powershell-adventures-in-cleaning-your-path\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6700\\\/more-powershell-adventures-in-cleaning-your-path\\\/#primaryimage\",\"url\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/04\\\/image_thumb-18.png\",\"contentUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/04\\\/image_thumb-18.png\",\"width\":1028,\"height\":250},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6700\\\/more-powershell-adventures-in-cleaning-your-path\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"PowerShell\",\"item\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/category\\\/powershell\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"More PowerShell Adventures in Cleaning Your Path\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/\",\"name\":\"The Lonely Administrator\",\"description\":\"Practical Advice for the Automating IT Pro\",\"publisher\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\",\"name\":\"Jeffery Hicks\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\",\"caption\":\"Jeffery Hicks\"},\"logo\":{\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"More PowerShell Adventures in Cleaning Your Path &#8226; The Lonely Administrator","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/","og_locale":"en_US","og_type":"article","og_title":"More PowerShell Adventures in Cleaning Your Path &#8226; The Lonely Administrator","og_description":"A few days ago, I posted an article that demonstrated a number of PowerShell techniques and concepts that you could use to clear out obsolete locations in your %PATH% environment variable. For those of you new to my blog I want to make sure you understand that I often use a scenario, such as this...","og_url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/","og_site_name":"The Lonely Administrator","article_published_time":"2019-04-24T12:50:49+00:00","article_modified_time":"2021-04-09T18:50:20+00:00","og_image":[{"url":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-18.png","type":"","width":"","height":""}],"author":"Jeffery Hicks","twitter_card":"summary_large_image","twitter_creator":"@JeffHicks","twitter_site":"@JeffHicks","twitter_misc":{"Written by":"Jeffery Hicks","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/#article","isPartOf":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/"},"author":{"name":"Jeffery Hicks","@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9"},"headline":"More PowerShell Adventures in Cleaning Your Path","datePublished":"2019-04-24T12:50:49+00:00","dateModified":"2021-04-09T18:50:20+00:00","mainEntityOfPage":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/"},"wordCount":1532,"commentCount":1,"publisher":{"@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9"},"image":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/#primaryimage"},"thumbnailUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-18.png","keywords":[".NET","functions","PowerShell"],"articleSection":["PowerShell","Scripting"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/","url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/","name":"More PowerShell Adventures in Cleaning Your Path &#8226; The Lonely Administrator","isPartOf":{"@id":"https:\/\/jdhitsolutions.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/#primaryimage"},"image":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/#primaryimage"},"thumbnailUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-18.png","datePublished":"2019-04-24T12:50:49+00:00","dateModified":"2021-04-09T18:50:20+00:00","breadcrumb":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/#primaryimage","url":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-18.png","contentUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/04\/image_thumb-18.png","width":1028,"height":250},{"@type":"BreadcrumbList","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6700\/more-powershell-adventures-in-cleaning-your-path\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"PowerShell","item":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},{"@type":"ListItem","position":2,"name":"More PowerShell Adventures in Cleaning Your Path"}]},{"@type":"WebSite","@id":"https:\/\/jdhitsolutions.com\/blog\/#website","url":"https:\/\/jdhitsolutions.com\/blog\/","name":"The Lonely Administrator","description":"Practical Advice for the Automating IT Pro","publisher":{"@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/jdhitsolutions.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9","name":"Jeffery Hicks","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg","url":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg","caption":"Jeffery Hicks"},"logo":{"@id":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg"}}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":8787,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/8787\/prerelease-of-psfunctiontools-for-powershell\/","url_meta":{"origin":6700,"position":0},"title":"Prerelease of PSFunctionTools for PowerShell","author":"Jeffery Hicks","date":"January 13, 2022","format":false,"excerpt":"At the end of last year wrote a series of blog posts describing tools and techniques for working with PowerShell scripts and functions. My goal was to build a framework of tools that I could use to automate PowerShell scripting work, such as creating a new module from a group\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/01\/psfunctiontools-commands.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/01\/psfunctiontools-commands.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/01\/psfunctiontools-commands.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/01\/psfunctiontools-commands.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/01\/psfunctiontools-commands.png?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/01\/psfunctiontools-commands.png?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":1869,"url":"https:\/\/jdhitsolutions.com\/blog\/scripting\/1869\/add-whatif-support-to-your-powershell-scripts\/","url_meta":{"origin":6700,"position":1},"title":"Add WhatIf Support to Your PowerShell Scripts","author":"Jeffery Hicks","date":"December 2, 2011","format":false,"excerpt":"In one of my recent articles for SMB IT, I included a PowerShell module. In the article I referenced that I included support for -Whatif in one of the functions. I was asked on Twitter to explain what I meant and how it works. So here goes. There are a\u2026","rel":"","context":"In &quot;PowerShell v2.0&quot;","block_context":{"text":"PowerShell v2.0","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell-v2-0\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":3117,"url":"https:\/\/jdhitsolutions.com\/blog\/scripting\/3117\/msdevwny-powershell-advanced-functions\/","url_meta":{"origin":6700,"position":2},"title":"MSDevWNY PowerShell Advanced Functions","author":"Jeffery Hicks","date":"June 20, 2013","format":false,"excerpt":"Last night I presented for the MSDevWNY user group in the Buffalo, NY area. They were an interested and enthusiastic audience and I think we could have spent another few hours talking about PowerShell. My presentation was one I've given before on Advanced PowerShell functions. I promised the group a\u2026","rel":"","context":"In &quot;Best Practices&quot;","block_context":{"text":"Best Practices","link":"https:\/\/jdhitsolutions.com\/blog\/category\/best-practices\/"},"img":{"alt_text":"talkbubble-v3","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2012\/05\/talkbubble-v3-150x150.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":8724,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/8724\/discovering-aliases-with-the-powershell-ast\/","url_meta":{"origin":6700,"position":3},"title":"Discovering Aliases with the PowerShell AST","author":"Jeffery Hicks","date":"December 15, 2021","format":false,"excerpt":"I've been working on a new PowerShell module that incorporates code from a few of my recent posts on converting PowerShell scripts and functions to files. I even whipped up a script, think of it as a meta-script, to create the module using the commands that I am adding to\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2021\/12\/find-alias-string.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2021\/12\/find-alias-string.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2021\/12\/find-alias-string.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2021\/12\/find-alias-string.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":2211,"url":"https:\/\/jdhitsolutions.com\/blog\/scripting\/2211\/powershell-scripting-with-validatepattern\/","url_meta":{"origin":6700,"position":4},"title":"PowerShell Scripting with [ValidatePattern]","author":"Jeffery Hicks","date":"April 19, 2012","format":false,"excerpt":"I've been writing about a number of parameters attributes you can include in your PowerShell scripting to validate parameter values. Today I want to cover using a regular expression pattern to validate a parameter value. I'm going to assume you have a rudimentary knowledge of how to use regular expressions\u2026","rel":"","context":"In &quot;PowerShell v2.0&quot;","block_context":{"text":"PowerShell v2.0","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell-v2-0\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2012\/04\/squarepattern-150x150.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":50,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/50\/background-jobs-in-powershell\/","url_meta":{"origin":6700,"position":5},"title":"Background jobs in PowerShell","author":"Jeffery Hicks","date":"August 31, 2006","format":false,"excerpt":"The *nix world has always had the ability to run jobs in the background. Now it's possible to do that in PowerShell. There is a terrific post on this at:http:\/\/jtruher.spaces.live.com\/Blog\/cns!7143DA6E51A2628D!130.entryYou'll need to copy some scripts and functions from the entry and save them locally. You also might need to tweak\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/6700","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/comments?post=6700"}],"version-history":[{"count":0,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/6700\/revisions"}],"wp:attachment":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/media?parent=6700"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/categories?post=6700"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/tags?post=6700"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}