【问题标题】:Check if a Windows service exists and delete in PowerShell检查 Windows 服务是否存在并在 PowerShell 中删除
【发布时间】:2022-02-15 03:13:20
【问题描述】:

我目前正在编写一个安装多个 Windows 服务的部署脚本。

服务名称是版本化的,因此我想在安装新服务时删除之前的 Windows 服务版本。

如何在 PowerShell 中最好地做到这一点?

【问题讨论】:

    标签: powershell windows-services


    【解决方案1】:

    您可以为此使用 WMI 或其他工具,因为在 Powershell 6.0 之前没有 Remove-Service cmdlet (See Remove-Service doc)

    例如:

    $service = Get-WmiObject -Class Win32_Service -Filter "Name='servicename'"
    $service.delete()
    

    或使用sc.exe 工具:

    sc.exe delete ServiceName
    

    最后,如果您确实可以访问 PowerShell 6.0:

    Remove-Service -Name ServiceName
    

    【讨论】:

    • 您也可以将此示例的相关部分移植到 powershell(使用 TransactedInstaller 类):eggheadcafe.com/articles/20060104.asp 但是 ravikanth 的方法可能更简单。
    • 最新版本的 PS 具有 Remove-WmiObject,并注意 $service.delete() 的静默失败 - 添加了另一个带有格式化示例的答案。
    • 简而言之,最新版本是以管理员身份运行 Powershell 并运行以下命令:$service = Get-WmiObject -Class Win32_Service -Filter "Name='servicename'"$service | Remove-WmiObject
    • 对于大家的信息,Straff 的回答说“注意 $service.delete() 的静默失败”
    • 从 Windows PowerShell 3.0 开始,cmdlet Get-WmiObject 已被 Get-CimInstance 取代。所以现在你可以这样做:Stop-Service 'servicename'; Get-CimInstance -ClassName Win32_Service -Filter "Name='servicename'" | Remove-CimInstance
    【解决方案2】:

    我发现在工作中使用正确的工具并没有什么坏处,我发现它正在运行(来自 Powershell)

    sc.exe \\server delete "MyService" 
    

    没有太多依赖的最可靠的方法。

    【讨论】:

    • .exe 部分非常重要,因为 sc 本身就是 Set-Content 的别名。
    • @tjrobinson 谢谢你,在我看到你的评论之前,我忽略了.exe。现在它为我工作。
    • 这仅在您有权访问远程计算机时才有用。如果不是(就像在大多数安全环境中一样),这将不起作用,您将需要支持传递凭据的东西
    • 如果服务是本地的,则服务器名称 (\\server) 将被省略。
    • 这更好,因为它更容易使用%$_ 编写脚本
    【解决方案3】:

    如果您只想检查服务是否存在:

    if (Get-Service "My Service" -ErrorAction SilentlyContinue)
    {
        "service exists"
    }
    

    【讨论】:

      【解决方案4】:

      我使用了“-ErrorAction SilentlyContinue”解决方案,但后来遇到了留下 ErrorRecord 的问题。所以这是另一种使用“Get-Service”检查服务是否存在的解决方案。

      # Determines if a Service exists with a name as defined in $ServiceName.
      # Returns a boolean $True or $False.
      Function ServiceExists([string] $ServiceName) {
          [bool] $Return = $False
          # If you use just "Get-Service $ServiceName", it will return an error if 
          # the service didn't exist.  Trick Get-Service to return an array of 
          # Services, but only if the name exactly matches the $ServiceName.  
          # This way you can test if the array is emply.
          if ( Get-Service "$ServiceName*" -Include $ServiceName ) {
              $Return = $True
          }
          Return $Return
      }
      
      [bool] $thisServiceExists = ServiceExists "A Service Name"
      $thisServiceExists 
      

      但是 ravikanth 有最好的解决方案,因为如果服务不存在,Get-WmiObject 不会抛出错误。所以我决定使用:

      Function ServiceExists([string] $ServiceName) {
          [bool] $Return = $False
          if ( Get-WmiObject -Class Win32_Service -Filter "Name='$ServiceName'" ) {
              $Return = $True
          }
          Return $Return
      }
      

      所以要提供更完整的解决方案:

      # Deletes a Service with a name as defined in $ServiceName.
      # Returns a boolean $True or $False.  $True if the Service didn't exist or was 
      # successfully deleted after execution.
      Function DeleteService([string] $ServiceName) {
          [bool] $Return = $False
          $Service = Get-WmiObject -Class Win32_Service -Filter "Name='$ServiceName'" 
          if ( $Service ) {
              $Service.Delete()
              if ( -Not ( ServiceExists $ServiceName ) ) {
                  $Return = $True
              }
          } else {
              $Return = $True
          }
          Return $Return
      }
      

      【讨论】:

      • 为了完整性,我决定在Get-WmiObject -Class Win32_Service -Filter "Name='$serviceName'"Get-Service $serviceName -ErrorAction Ignore 之间进行速度比较(如果服务不存在,则完全隐藏错误)。我预计 Get-WmiObject 可能会更快,因为它不会引发错误。我错了。每个循环运行 100 次,Get-Service 耗时 0.16 秒,而 Get-WmiObject 耗时 9.66 秒。所以 Get-Service 比 Get-WmiObject 快 60 倍。
      【解决方案5】:

      最新版本的 PS 具有 Remove-WmiObject。注意 $service.delete() 的静默失败 ...

      PS D:\> $s3=Get-WmiObject -Class Win32_Service -Filter "Name='TSATSvrSvc03'"
      
      PS D:\> $s3.delete()
      ...
      ReturnValue      : 2
      ...
      PS D:\> $?
      True
      PS D:\> $LASTEXITCODE
      0
      PS D:\> $result=$s3.delete()
      
      PS D:\> $result.ReturnValue
      2
      
      PS D:\> Remove-WmiObject -InputObject $s3
      Remove-WmiObject : Access denied 
      At line:1 char:1
      + Remove-WmiObject -InputObject $s3
      + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          + CategoryInfo          : InvalidOperation: (:) [Remove-WmiObject], ManagementException
          + FullyQualifiedErrorId : RemoveWMIManagementException,Microsoft.PowerShell.Commands.RemoveWmiObject
      
      PS D:\> 
      

      对于我的情况,我需要以“管理员身份”运行

      【讨论】:

        【解决方案6】:

        在Powershell 5.0中删除多个服务,因为这个版本不存在remove service

        运行以下命令

        Get-Service -Displayname "*ServiceName*" | ForEach-object{ cmd /c  sc delete $_.Name}
        

        【讨论】:

          【解决方案7】:

          可以使用 Where-Object

          if ((Get-Service | Where-Object {$_.Name -eq $serviceName}).length -eq 1) { "Service Exists" }

          【讨论】:

            【解决方案8】:

            结合 Dmitri 和 dcx 的答案,我做了这个:

            function Confirm-WindowsServiceExists($name)
            {   
                if (Get-Service $name -ErrorAction SilentlyContinue)
                {
                    return $true
                }
                return $false
            }
            
            function Remove-WindowsServiceIfItExists($name)
            {   
                $exists = Confirm-WindowsServiceExists $name
                if ($exists)
                {    
                    sc.exe \\server delete $name
                }       
            }
            

            【讨论】:

              【解决方案9】:

              单机:

              if (Get-Service "service_name" -ErrorAction 'SilentlyContinue'){(Get-WmiObject -Class Win32_Service -filter "Name='service_name'").delete()}
              
              else{write-host "No service found."}
              

              PC 列表的宏:

              $name = "service_name"
              
              $list = get-content list.txt
              
              foreach ($server in $list) {
              
              if (Get-Service "service_name" -computername $server -ErrorAction 'SilentlyContinue'){
              (Get-WmiObject -Class Win32_Service -filter "Name='service_name'" -ComputerName $server).delete()}
              
              else{write-host "No service $name found on $server."}
              
              }
              

              【讨论】:

                【解决方案10】:

                要检查名为 MySuperServiceVersion1 的 Windows 服务是否存在,即使您可能不确定它的确切名称,您也可以使用通配符,使用如下子字符串:

                 if (Get-Service -Name "*SuperService*" -ErrorAction SilentlyContinue)
                {
                    # do something
                }
                

                【讨论】:

                  【解决方案11】:

                  PowerShell Core (v6+) 现在有一个Remove-Service cmdlet

                  我不知道将其反向移植到 Windows PowerShell 的计划,它在 v5.1 中可用。

                  例子:

                  # PowerShell *Core* only (v6+)
                  Remove-Service someservice
                  

                  请注意,如果服务不存在,则调用将失败,因此仅在当前存在时将其删除,您可以这样做:

                  # PowerShell *Core* only (v6+)
                  $name = 'someservice'
                  if (Get-Service $name -ErrorAction Ignore) {
                    Remove-Service $name
                  }
                  

                  【讨论】:

                  • 在 PowerShell 版本 7 上,使用对 RemoteService 的调用会收到错误“Remove-Service : The term 'Remove-Service' is not known as a cmdlet, function, script file, or operable程序”
                  • @PedroVicente,请注意,在远程处理的上下文中,即使 PowerShell 7 仍然针对远程计算机上的 Windows PowerShell - 请参阅 this answer
                  【解决方案12】:

                  对此进行了修改以获取服务器的输入列表,指定主机名并提供一些有用的输出

                              $name = "<ServiceName>"
                              $servers = Get-content servers.txt
                  
                              function Confirm-WindowsServiceExists($name)
                              {   
                                  if (Get-Service -Name $name -Computername $server -ErrorAction Continue)
                                  {
                                      Write-Host "$name Exists on $server"
                                      return $true
                                  }
                                      Write-Host "$name does not exist on $server"
                                      return $false
                              }
                  
                              function Remove-WindowsServiceIfItExists($name)
                              {   
                                  $exists = Confirm-WindowsServiceExists $name
                                  if ($exists)
                                  {    
                                      Write-host "Removing Service $name from $server"
                                      sc.exe \\$server delete $name
                                  }       
                              }
                  
                              ForEach ($server in $servers) {Remove-WindowsServiceIfItExists($name)}
                  

                  【讨论】:

                    【解决方案13】:
                    • 对于 v6 之前的 PowerShell 版本,您可以这样做:

                      Stop-Service 'YourServiceName'; Get-CimInstance -ClassName Win32_Service -Filter "Name='YourServiceName'" | Remove-CimInstance
                      
                    • 对于 v6+,您可以使用the Remove-Service cmdlet

                    请注意,从 Windows PowerShell 3.0 开始,the cmdlet Get-WmiObject 已被 Get-CimInstance 取代。

                    【讨论】:

                      【解决方案14】:

                      Windows Powershell 6 将具有 Remove-Service cmdlet。 截至目前,Github 版本显示 PS v6 beta-9

                      来源:https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/remove-service?view=powershell-6

                      【讨论】:

                        【解决方案15】:

                        我知道这是一个老问题,但如果有人正在寻找单线:

                        在 PS 版本 > 7.2

                        Name 属性上的通配符搜索

                        Get-Service *name* | Select-Object -First 1 | Remove-Service

                        搜索显示名称属性

                        Get-Service -DisplayName "My Service Description" | Remove-Service

                        【讨论】:

                          猜你喜欢
                          • 2015-10-31
                          • 1970-01-01
                          • 2018-09-24
                          • 2010-11-03
                          • 1970-01-01
                          • 1970-01-01
                          • 2014-03-02
                          • 2013-07-09
                          • 1970-01-01
                          相关资源
                          最近更新 更多