当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 清理你的道路的更多 PowerShell 冒险

作者:精品下载站 日期:2024-12-14 07:55:09 浏览:13 分类:玩电脑

清理你的道路的更多 PowerShell 冒险


几天前,我发布了一篇文章,演示了许多 PowerShell 技术和概念,您可以使用它们来清除 %PATH% 环境变量中的过时位置。对于那些刚接触我的博客的人,我想确保您了解我经常使用一个场景(例如这个场景)来演示和教授 PowerShell。不要假设任何功能或代码示例都是完整的并且可以投入生产。 \发帖后,我听到很多人建议使用 .NET Framework 中的 SetEnvironmentVariable() 方法,特别是因为我使用该框架来发现路径分割器。那么让我们回到当前的问题并探索这种方法。

Cmdlet 优先

我有一个我拥护的 PowerShell 脚本哲学,特别是对于初学者。如果有一个 cmdlet 可以实现任务,请通过 .NET Framework 使用它。我的意思是,使用 Get-Date cmdlet 来获取当前日期和时间,而不是 [datetime]::Now。有几个原因。

首先,我不希望 PowerShell 初学者觉得需要成为 .NET 开发人员来编写脚本和函数。 PowerShell 的全部要点在于存在大量抽象,因此脚本编写者不必了解 .NET Framework。 Cmdlet 更容易发现。它们被记录下来。最重要的是,在我们使用的场景中,应该支持-WhatIf。如果运行 .NET 方法,则没有内置安全性。它运行了。不过,您可以添加对 -WhatIf 的支持,我稍后会介绍。

是的,在某些时候,您可能会求助于 .NET Framework 来提高性能或在没有 cmdlet 的情况下完成某些任务。但这是在你获得了一些经验之后。并且不要忘记,您无法直接对 .NET Framework 进行 Pester 测试。无法模拟 [Environment]::SetEnvironmentVariable()。跳转到 .NET Framework 并没有什么问题。我只是认为你需要一个很好的理由,并确保添加大量的代码文档。

探索 .NET 类

考虑到这一切,让我逐步完成使用 [system.environment] 类构建一些 PowerShell 工具的过程。我们假设您在某处读到过或在 Stack Overflow 问题中看到过它。您可以而且可能应该搜索 system.enviroment,这应该会让您进入 Microsoft 文档页面。或者您可以尝试在 PowerShell 中探索它。在提示符下键入 [System.Environment]:: 并按空格键。您可以循环访问静态属性和方法。或者,如果您加载了 PSReadline 模块,请按 Ctrl+Space

[玩转系统] 清理你的道路的更多 PowerShell 冒险

顺便说一句,从技术上讲,您不需要包含系统前缀。如果您只输入 [Environment]:: PowerShell 知道您指的是 System.Environment。

查看列表,有一个名为 GetEnvironmentVariable 的东西。 PowerShell 可能会插入一个左括号,这是一个方法的线索。如果您使用 PSReadline,您可以看到方法定义。或者直接按 Enter 键,不带()。

[玩转系统] 清理你的道路的更多 PowerShell 冒险

输出表明有两种方法可以使用此方法,每种方法都会生成一个字符串。第一个采用变量的名称。这似乎很简单,可以测试。

[玩转系统] 清理你的道路的更多 PowerShell 冒险

第二种方法采用第二个参数。如果您看到的类型不是 string、int 或 boolean 等典型类型,那么它很可能是枚举。您可以使用相同的技术来发现可能的值。

[玩转系统] 清理你的道路的更多 PowerShell 冒险

这是有道理的,因为您可以拥有用户或系统环境变量。咱们试试吧。

[玩转系统] 清理你的道路的更多 PowerShell 冒险

我不需要专门指定“用户”或“机器”。因为它是第二个参数,所以 PowerShell 知道它应该是什么。

测试%PATH%

考虑到这一点,让我们转向验证路径。这里有一些命令将成为功能的基础。

[System.Environment]::GetEnvironmentVariable("PATH", "User") -split ";" | where-object { $_ -AND -Not (Test-Path $_) }
[System.Environment]::GetEnvironmentVariable("PATH", "Machine") -split ";" | where-object {$_ -AND -Not (Test-Path $_) }

我的Where-Object 语句还过滤掉了变量以分号结尾时可能出现的空白。

[玩转系统] 清理你的道路的更多 PowerShell 冒险

我可以只关注字符串值——比如不存在的路径——但我总是喜欢考虑管道中的丰富对象。我可能会以我从未想过的方式消费和重复使用的东西。考虑到这一点,我编写了这个函数。

Function Get-EnvPath {
    [cmdletbinding()]
    [OutputType("myEnvPath")]
    Param(
        [ValidateSet("All","User","System")]
        [string]$Scope = "All"
    )

    #get the path separator character specific to this operating system
    $splitter = [System.IO.Path]::PathSeparator

    if ($scope -ne "System") {

        Write-Verbose "Validating USER paths"

        #filter out blanks if path ends in a splitter
        [System.Environment]::GetEnvironmentVariable("PATH", "User") -split $splitter |
        Where-Object { $_ } | Foreach-Object {
            # create a custom object based on each path
            Write-Verbose "  $_"
            [pscustomobject]@{
                PSTypeName   = "myEnvPath"
                Scope = "User"
                Computername = [System.Environment]::MachineName
                UserName     = [System.Environment]::UserName
                Target       = "User"
                Path         = $_
                Exists       = Test-Path $_
            }
        } #foreach
    }
    if ($Scope -ne "User") {
        Write-Verbose "Validating MACHINE paths"
        [System.Environment]::GetEnvironmentVariable("PATH", "Machine") -split $splitter |
        Where-Object { $_ } | Foreach-Object {
            # create a custom object based on each path
            Write-Verbose "  $_"
            [pscustomobject]@{
                PSTypeName   = "myEnvPath"
                Scope        = "System"
                Computername = [System.Environment]::MachineName
                UserName     = [System.Environment]::UserName
                Target       = "Machine"
                Path         = $_
                Exists       = Test-Path $_
            }
        } #foreach
    }
} #end function

我就合适的动词进行了辩论。在上一篇文章中,我使用了 Test 动词。但我想得越多,我意识到我真正在做的是获取一堆反映 %PATH% 中每个位置状态的对象。可能有人认为测试路径功能应该单独处理,但由于我知道这是我编写此函数的主要原因,所以我不介意将其包含在这里。现在我有了一个易于使用的功能。

[玩转系统] 清理你的道路的更多 PowerShell 冒险

修复 %PATH% 变量

下半场的挑战是去除不好的地点。这是我可以使用 SetEnvironmentVariable() 方法的地方。如果您使用我展示的技术,您会发现您需要提供变量的名称、新路径和目标。像这样的东西:

[System.Environment]::SetEnvironmentVariable("PATH",$revised,"User")

但是,正如我提到的,不支持 -WhatIf 或其他安全检查。如果我运行该命令,PowerShell 会很乐意执行它。我想记住这一点。我还想创建一个可以使用管道中的对象的函数。让我向您展示我的想法,然后我们将进行讨论。

Function Repair-EnvPath {
     [cmdletbinding(SupportsShouldProcess)]
     Param(
         [Parameter(Position = 0, Mandatory, ValueFromPipelineByPropertyName)]
         [string]$Path,
         [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
         [ValidateSet("User","Machine")]
         [string]$Target
     )
    Begin {
        Write-Verbose "Starting $($myinvocation.MyCommand)"
        #get the path separator character specific to this operating system
        $splitter = [System.IO.Path]::PathSeparator
    }

    Process {
        Write-Verbose "Removing $path from Target %PATH% setting"
        #get current values as an array
        $paths = [system.environment]::GetEnvironmentVariable("PATH", $Target) -split $splitter
        $Corrected = (($paths).where( { $_ -ne $Path })) -join $splitter
        Write-Verbose "Setting a new value of $corrected"
        #add code support for -WhatIf
        if ($PSCmdlet.ShouldProcess("$Target %Path% variable", "Remove $Path")) {
            [System.Environment]::SetEnvironmentVariable("PATH", $Corrected, $Target)
        }
    }
    End {
        Write-Verbose "Ending $($myinvocation.MyCommand)"
    }

 }

我再次尝试选择一个有意义的动词。可能有人认为 Set-EnvPath 应该与 Get-EnvPath 一起使用。或者也许是 Update-EnvPath。对我来说,我正在使用这个命令来修复问题。

首先要注意的是 [cmdletbinding()]。我告诉 PowerShell 在该函数中自动包含对 -WhatIf 的支持。我可以运行 Repair-EnvPath -Whatif,并且 ShouldProcess 指令将传递到也支持 -WhatIf 的函数中的任何 cmdlet。但是,该函数中的大部分代码都是本机 .NET。我得自己处理。

该函数采用两个强制参数:路径字符串和环境目标。我已经对后者进行了验证测试。这意味着用户只能输入用户或机器。作为额外的好处,PowerShell 会将这些选项呈现为选项卡完整选项。

请注意,参数名称与 Get-EnvPath 函数的某些属性相同。因为我已经告诉 PowerShell 使用通过属性名称绑定这些参数,当函数看到具有 Path 属性的对象时,它将使用该值。塔吉特也是如此。

Process 脚本块从给定目标获取所有路径并将它们分割成一个数组。

$paths = [system.environment]::GetEnvironmentVariable("PATH", $Target) -split $splitter

然后我可以构建不包含“坏”路径的新路径字符串。

$Corrected = (($paths).where( { $_ -ne $Path })) -join $splitter

现在进行最后一步。我需要调用 SetEnvironmentVariable 方法,但我需要添加对 -WhatIf 的支持。

编写你自己的假设

编写 PowerShell 脚本或函数时,您有一个由 $PSCmdlet 引用的内置对象。该对象有一个名为 ShouldProcess() 的方法。要使用,请创建一个 IF 语句。如果检测到 -Whatif,IF 块中的代码将会运行。

If ($PSCmdlet.ShouldProcess("target")) {
    #do something serious
}

如果 PowerShell 检测到运行脚本的人使用了 -WhatIf,则 PowerShell 将使用 ShouldProcess 方法中的参数来创建您看到的消息:“What if: Performing the operation on target :”通常,您需要做的就是为目标提供一个值,例如路径、名称或您正在修改的任何内容。但是,您可以进行更多控制,因为还有一些其他参数选项。

[玩转系统] 清理你的道路的更多 PowerShell 冒险

我经常也会定义动作。这就是我在代码中所做的。

if ($PSCmdlet.ShouldProcess("$Target %Path% variable", "Remove $Path")) {
  [System.Environment]::SetEnvironmentVariable("PATH", $Corrected, $Target)
}

正如您所看到的,您可以对目标和行动值进行创意。

[玩转系统] 清理你的道路的更多 PowerShell 冒险

不要忘记,您需要 [cmdletbinding()] 中的 SupportsShouldProcess 指令和 If 语句。顺便说一句,您可以在代码中添加任意数量的 WhatIf 语句。

清理我的道路

考虑到所有这些,以下是我如何使用我的命令。

[玩转系统] 清理你的道路的更多 PowerShell 冒险

请注意,它们是为协同工作而编写的。我可能会考虑修改 Get-EnvPath 函数以包含 Target 的过滤选项以及路径是否存在。创建 PowerShell 工具时,您需要考虑谁将使用它、如何使用以及他们的期望。由于我最有可能使用此工具来清理过时的路径,因此最好包含一个过滤选项,这样我就不必记住使用中间的Where-Object 表达式。但如果你有兴趣的话,我会把这个留给你。否则,是时候清理了。

[玩转系统] 清理你的道路的更多 PowerShell 冒险

您甚至可以考虑对 Repair-EnvPath 进行一些增强,例如提供有关删除内容的反馈或添加 -Passthru 参数。再说一次,我会把这个乐趣留给你。

我仍然更喜欢尽可能使用 cmdlet 和本机 PowerShell,但 .NET Framework 可以填补空白。您是否负责任地使用它取决于您。

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯