想要一种使用 PowerShell 将不同字符串拆分为多个部分的通用方法

Posted

技术标签:

【中文标题】想要一种使用 PowerShell 将不同字符串拆分为多个部分的通用方法【英文标题】:Would like a universal way to split different strings into multiple parts using PowerShell 【发布时间】:2020-09-13 08:43:24 【问题描述】:

我正在研究一种从注册表中读取软件的 UninstallStrings 的方法。然后我尝试执行这些字符串来卸载软件。

当我打印出保存字符串信息的变量时,它会正确打印整个字符串(带有参数),但我无法运行这些字符串。 这个问题有多个部分。

一些安装字符串的格式是这样的

c:\file path\blah\app.exe /uninstall
"c:\file path\blah\app.exe" /uninstall
c:\file path\blah\app.exe --uninstall
'c:\file path\blah\app.exe' /uninstall

我想做的是找出能够以“通用”方式运行卸载程序的最佳方式。 有没有办法有效地做到这一点?

我尝试了两种不同的方式来执行字符串。

    & $uninstaller

    Start-Process -FilePath cmd.exe -ArgumentList '/c', $uninstaller -Wait 

它们似乎都不起作用。没有错误,但它们似乎没有运行,因为当我检查应用程序时它仍然安装。

我尝试用几种方法拆分文本。


    $Uninstaller.split("/")[0]
    $Uninstaller.split("/",2)[1]
    $($Uninstaller) | Invoke-Expression
    $Uninstaller.Substring(0,$Uninstaller.lastIndexOf('.exe '))
    $Uninstaller.split('^(*?\.exe) *')

提前致谢!

【问题讨论】:

我猜您需要用双引号将app.exe 的完整路径括起来?例如。后者为'c:\file path\blah\app.exe' /uninstall"c:\file path\blah\app.exe" /uninstall? 感谢您的回复,问题是,并非注册表中的所有 UninstallStrings 都采用该语法。这就是让这有点复杂的原因。一些卸载字符串是 msiexec,另一些是带有自定义参数的可执行卸载程序。并不是所有的论点都是普遍的。它们基于可执行文件。一个 exe 可能有破折号,另一个是正斜杠。 【参考方案1】:

想通了。也许有更好的方法,但这似乎对我有用。

CLS

$Software = "OneDrive"
$Filter = "*" + $Software + "*"
$Program = $ProgUninstall = $FileUninstaller = $FileArg = $NULL

try 

    if (Test-Path -Path "HKLM:\SOFTWARE\WOW6432Node") 
    
        $programs = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction Stop
    

    $programs += Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction Stop
    $programs += Get-ItemProperty -Path "Registry::\HKEY_USERS\*\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue
 
catch 

    Write-Error $_
    break


foreach($Program in $Programs)

    $ProgDisplayName = $Program.DisplayName
    $ProgUninstall = $Program.UninstallString

    if($ProgDisplayName -like $Filter)
    
        if($ProgUninstall -like "msiexec*")
        
            $FileUninstaller = $ProgUninstall.split(" ")[0]
            $FileArg = ($($ProgUninstall).split(" ",2)[1])
        
        else
        
            if(($ProgUninstall -like '"*"*') -or ($ProgUninstall -like "'*'*"))
            
                #String has quotes, don't need to do anything

            
            else
            
                if($NULL -ne $ProgUninstall)
                
                    #String doesn't have quotes so we should add them
                    $ProgUninstall = '"' + ($ProgUninstall.Replace('.exe','.exe"'))                    
                
            

            #Let's grab the uninstaller and arguments
            $FileUninstaller = $ProgUninstall.split('"')[1]
            $FileArg = $ProgUninstall.split('"')[-1]
        

        #Debug
        #$FileUninstaller
        #$FileArg

        #Run the Uninstaller
        Start-Process $FileUninstaller -ArgumentList $FileArg -wait -ErrorAction SilentlyContinue
    

【讨论】:

【参考方案2】:

这对于 msi 安装非常容易:

get-package *whatever* | uninstall-package

这是一个非 msi 静默卸载示例,但您必须添加“/S”或任何静默卸载选项:

get-package notepad++* | 
  %  & $_.Meta.Attributes['UninstallString'] /S 

【讨论】:

感谢您的更新回复,但就我的情况而言,我希望它尽可能无缝。我在下面发布了一个我认为对我很有效的解决方案,但 JoesefZ 的解决方案似乎更好。它与我的输出完全相同(这是我需要的),代码更少,所以我觉得这是最好的答案。【参考方案3】:

尝试以下测试脚本,其中简单的控制台应用程序CliParser.exe 仅回显命令行参数(用C 编写,灵感来自here)。使用名称中带有空格的精确副本CliParser noPause.exe;它表明 . $Uninstaller $UninsParams 应该可以工作,即使路径 $Uninstaller 包含空格。

无需使用Start-Process -FilePath cmd.exe …;只需将点源运算符应用为. $Uninstaller $UninsParams...

$lines 集合包含几乎具有代表性的卸载字符串语法模式(取自我的 Windows 10):

$lines = @'
MsiExec.exe /XFFC6E93A-B9AD-3F20-9B06-EE20E24AAEAF
"D:\Program Files (x86)\Virtual Russian Keyboard\unins000.exe"
"C:\Windows10Upgrade\Windows10UpgraderApp.exe" /Uninstall
C:\Windows\SysWOW64\msiexec.exe /package CFEF48A8-BFB8-3EAC-8BA5-DE4F8AA267CE /uninstall 815F0BC1-7E54-300C-9ACA-C9460FDF6F78 /qb+ REBOOTPROMPT=""
C:\Program Files (x86)\InstallShield Installation Information\8833FFB6-5B0C-4764-81AA-06DFEED9A476\Setup.exe -runfromtemp -removeonly
"C:\WINDOWS\SysWOW64\RunDll32.EXE" "C:\Program Files\NVIDIA Corporation\Installer2\InstallerCore\NVI2.DLL",UninstallPackage Display.Driver
%windir%\system32\sdbinst.exe -u "C:\WINDOWS\AppPatch\CustomSDB\9f4f4a9b-eec5-4906-92fe-d1f43ccf5c8d.sdb"
'@ -split [System.Environment]::NewLine

$FakeUninstaller = "D:\bat\CLIParser noPause.exe"

foreach ( $line in $lines ) 
    # $line
    $aux = $line -split @('\.exe'),2,[System.StringSplitOptions]::None
    $Uninstaller = (cmd /c echo $($aux[0].TrimStart('"').TrimStart("'") + '.exe')).Trim('"')
    $UninsParams = $aux[1].TrimStart('"').TrimStart("'").Trim().split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)

    ". $Uninstaller $UninsParams"
    . $FakeUninstaller $UninsParams | Where-Object  $_ -notlike "param 0 = *" 

输出D:\PShell\SO\62023960.ps1

. MsiExec.exe /XFFC6E93A-B9AD-3F20-9B06-EE20E24AAEAF 参数 1 = /XFFC6E93A-B9AD-3F20-9B06-EE20E24AAEAF . “D:\Program Files (x86)\Virtual Russian Keyboard\unins000.exe” . C:\Windows10Upgrade\Windows10UpgraderApp.exe /卸载 参数 1 = /卸载 . C:\Windows\SysWOW64\msiexec.exe /package CFEF48A8-BFB8-3EAC-8BA5-DE4F8AA267CE /uninstall 815F0BC1-7E54-300C-9ACA-C9460FDF6F78 /qb+ REBOOTPROMPT="" 参数 1 = /包 参数 2 = CFEF48A8-BFB8-3EAC-8BA5-DE4F8AA267CE 参数 3 = /卸载 参数 4 = 815F0BC1-7E54-300C-9ACA-C9460FDF6F78 参数 5 = /qb+ 参数 6 = REBOOTPROMPT="" . “C:\Program Files (x86)\InstallShield 安装信息\8833FFB6-5B0C-4764-81AA-06DFEED9A476\Setup.exe”-runfromtemp -removeonly 参数 1 = -runfromtemp 参数 2 = -removeonly . C:\WINDOWS\SysWOW64\RunDll32.exe "C:\Program Files\NVIDIA Corporation\Installer2\InstallerCore\NVI2.DLL",UninstallPackage Display.Driver 参数 1 = C:\Program Files\NVIDIA Corporation\Installer2\InstallerCore\NVI2.DLL,UninstallPackage 参数 2 = Display.Driver . C:\WINDOWS\system32\sdbinst.exe -u "C:\WINDOWS\AppPatch\CustomSDB\9f4f4a9b-eec5-4906-92fe-d1f43ccf5c8d.sdb" 参数 1 = -u 参数 2 = C:\WINDOWS\AppPatch\CustomSDB\9f4f4a9b-eec5-4906-92fe-d1f43ccf5c8d.sdb

【讨论】:

感谢您的分享。这实际上似乎比我在下面发布的解决方案要好。它基本上用更少的代码行做同样的事情,所以我更喜欢你的。我唯一不明白的是最后一行(卸载程序),它似乎对我不起作用。我尝试使用 & $file,但仍然没有。无论如何,我通过使用 Start-Process $Uninstaller -ArgumentList $UninsParams -wait 让它工作 . $Uninstaller $UninsParams 可能会失败,因为如果echoed 字符串包含空格,cmd 会添加双引号。例如,$y='a b'; echo $y; cmd /c echo $y; cmd /c "echo $y" 返回字符串a b"a b"a b。但是,正确转义 cmd /c echo $($aux[0].TrimStart('"').TrimStart("'") + '.exe') 中的所有双引号似乎是一个棘手的问题。可以简单地使用一个额外的辅助变量来完成,或者作为$Uninstaller = (cmd /c echo $($aux[0].TrimStart('"').TrimStart("'") + '.exe')).Trim('"'),查看更新的答案。 您好,我做了调整。您拥有的最后一行,我使用以下命令进行了调整以适合我的示例: 。 $卸载程序 $UninsParams | Where-Object $_ -notlike "param 0 = *" 但是,我收到此错误:". : The term '"C:\Program Files (x86)\Microsoft OneDrive\20.064.0329.0008_7\OneDriveSetup.exe" ' 未被识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包含路径,请验证路径是否正确,然后重试。" 我的错误,对不起。调整后(答案)在$Uninstaller = (cmd /c echo $($aux[0].TrimStart('"').TrimStart("'") + '.exe')).Trim('"') 中缺少最左括号…

以上是关于想要一种使用 PowerShell 将不同字符串拆分为多个部分的通用方法的主要内容,如果未能解决你的问题,请参考以下文章

Powershell 创建不同的 Zip 文件?

如何解析csv文件,查找触发器并使用PowerShell拆分成新文件

PowerShell 在不同目录中查找和替换

如何在PowerShell脚本中嵌入EXE文件

想要将 PSList 包装在 Powershell 函数中以使用管道值

powershell 分割逗号分隔字符串和使用PowerShell修剪项目的三种不同方法