Powershell 脚本用于查找并安装软件(如果不存在)

Powershell 脚本用于查找并安装软件(如果不存在)

我正在尝试编写一个 powershell 脚本,用于查找某些程序,如果找不到,则安装它们。我使用 chocolatey 来执行此操作,似乎效果很好。以下是我目前所拥有的:

if (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal
.WindowsBuiltInRole] "Administrator")) { Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs; exit }

Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

$tempdir = Get-Location
$tempdir = $tempdir.tostring()
$FirstAppToMatch = '*Google Earth*'
$SecondAppToMatch = '*Google Chrome*'
$ThirdAppToMatch = '*FireFox*'
$FourthAppToMatch = '*Notepad++*'
$FifthAppToMatch = '*Adobe Reader*'
$SixthAppToMatch = '*Office*'
$msiFile = $tempdir+"\microsoft.interopformsredist.msi"
$msiArgs = "-qb"

function Get-InstalledApps
{
if ([IntPtr]::Size -eq 4) {
    $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
    $regpath = @(
        'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )
}
Get-ItemProperty $regpath | .{process{if($_.DisplayName -and 
$_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, 
DisplayVersion, UninstallString |Sort DisplayName
}

$result = Get-InstalledApps | where {$_.DisplayName -like $FirstAppToMatch}

If ($result -eq $null) {
(cinst googleearthpro -y)
}

Write-Host "Press any key to continue ..."

$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

{
if ([IntPtr]::Size -eq 4) {
    $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
$regpath = @(
        'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )
}
Get-ItemProperty $regpath | .{process{if($_.DisplayName -and 
$_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, 
DisplayVersion, UninstallString |Sort DisplayName
}

$result = Get-InstalledApps | where {$_.DisplayName -like $SecondAppToMatch}

If ($result -eq $null) {
(cinst googlechrome -y)
}

Write-Host "Press any key to continue ..."

$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

{
if ([IntPtr]::Size -eq 4) {
    $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
    $regpath = @(
        'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )
}
Get-ItemProperty $regpath | .{process{if($_.DisplayName -and 
$_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, 
DisplayVersion, UninstallString |Sort DisplayName
}

$result = Get-InstalledApps | where {$_.DisplayName -like $ThirdAppToMatch}

If ($result -eq $null) {
(cinst firefox -y)
}

Write-Host "Press any key to continue ..."

$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

{
if ([IntPtr]::Size -eq 4) {
    $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
    $regpath = @(
        'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )
}
Get-ItemProperty $regpath | .{process{if($_.DisplayName -and 
$_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, 
DisplayVersion, UninstallString |Sort DisplayName
}

$result = Get-InstalledApps | where {$_.DisplayName -like $FourthAppToMatch}

If ($result -eq $null) {
(cinst notepadplus -y)
}

Write-Host "Press any key to continue ..."

$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

{
if ([IntPtr]::Size -eq 4) {
    $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
    $regpath = @(
        'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )
}
Get-ItemProperty $regpath | .{process{if($_.DisplayName -and 
$_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, 
DisplayVersion, UninstallString |Sort DisplayName
}

$result = Get-InstalledApps | where {$_.DisplayName -like $FifthAppToMatch}

If ($result -eq $null) {
(cinst adobereader -y)
}

Write-Host "Press any key to continue ..."

$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

{
if ([IntPtr]::Size -eq 4) {
    $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
    $regpath = @(
        'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )
}
Get-ItemProperty $regpath | .{process{if($_.DisplayName -and 
$_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, 
DisplayVersion, UninstallString |Sort DisplayName
}

$result = Get-InstalledApps | where {$_.DisplayName -like $SixthAppToMatch}

If ($result -eq $null) {
(cinst office365business -y)
}

Write-Host "Press any key to continue ..."

$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

所以我有两个问题。第一个是第一个“write-host”之后的所有内容都会显示出来。我知道我可以隐藏窗口,但为了测试和调试代码,我不想这样做。我尝试用 { } 封装所有 write-host 命令,但它仍然显示了所有代码,例如,当脚本运行时,我会看到 PS 窗口中显示的所有命令。如果这是批处理,我只需 @echo off,但我不确定如何在 powershell 中执行此操作。

第二个问题是它很混乱。虽然它能工作,但是很混乱。我觉得我应该能够将这段代码压缩一些,只是我不知道该怎么做。在这种情况下,我需要它遍历每个“应用程序”,然后如果在系统上找不到该特定“应用程序”,则安装不同的程序。因此,它会查找谷歌地球,如果找不到,它会运行命令来安装它...然后它会循环回到下一个应用程序...找不到它,然后运行不同的命令来安装它,依此类推。不过,我遇到了这个问题。我一直在研究 for 循环和数组,试图看看除了像我在这里一样一遍又一遍地列出每个命令之外,我是否能找到更好的方法,但似乎 for 循环和 for-each 循环旨在对数组中的每个元素执行相同的操作。在这种情况下,我需要它做一些不同的事情,即根据它正在寻找的应用程序运行不同的命令或不运行不同的命令。

任何提示或建议都将不胜感激。提前致谢。

答案1

1-花括号。

有很多不必要的配对。看看它们能造成什么不同:

PS C:\> Write-Host "foo"
foo
PS C:\> {Write-Host "foo"}
Write-Host "foo"

代码中的重复if/elseGet-ItemProperty部分始终用一对花括号括起来。删除这些多余的括号。它们是导致代码出现在控制台输出中的原因。看起来它们是从脚本开头粘贴函数内容的结果。有关花括号/方括号/圆括号的更多信息:http://www.powertheshell.com/scriptblock/

2-这是主观的。

一个通用提示:提高缩进的一致性。排除代码故障可能很难。排除混乱的代码故障更难。在编写时提高可读性可以让事情变得更容易。

例如,如果缩进保持得更加一致,那么在回答您的第一个问题时我提到的代码块中的额外括号可能更容易被识别。

原始区块(已删除):

{
if ([IntPtr]::Size -eq 4) {
    $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
    $regpath = @(
        'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )
}
...
}

这次改进了缩进:

{
    if ([IntPtr]::Size -eq 4) {
        $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
    }
    else {
        $regpath = @(
            'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

            'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
        )
    }
    ...
}

导致此脚本混乱的最大因素是重复使用此代码块。脚本以一个函数开始。这很好,因为您将多次调用该函数的内容。这就是函数的用途。但是,函数的内容在整个脚本中被重复调用。无需不断设置变量$regpath并用 查询它Get-ItemProperty。这就是您的函数所做的。删除这些块会将您的代码减少一半。

您说得对 - 循环和数组可以在很多方面帮助您编写代码。保留您的函数,您可以像这样循环遍历每个应用程序:

# the index of each App matches the installer name
$apps = @('Google Earth','Google Chrome','FireFox','Notepad++','Adobe Reader','Office')
$name = @('googleearthpro','googlechrome','firefox','notepadplus','adobereader','office365business')

foreach($app in $apps) {

    $result = Get-InstalledApps | Where-Object { $_.DisplayName -like $app }

    if($result -eq $null) {

        # get the index for the installer for this app
        $i = $apps.IndexOf($app)
        Write-Host "$app not found. Installing" $name[$i]

        (cinst $name[$i] -y)
    }

}

相关内容