Skrypt PowerShell do wyszukiwania i instalowania oprogramowania, jeśli nie jest obecne

0

Próbuję napisać skrypt PowerShell, który wyszukuje określone programy, a jeśli ich nie znajdzie, instaluje je. Używam do tego Chocolatey i wydaje się, że działa całkiem dobrze. Oto co mam do tej pory:

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")

Mam więc 2 problemy. Po pierwsze, wyświetla się wszystko po pierwszym „hostie zapisu”. Wiem, że mogę ukryć okno, ale nie chcę tego robić w celu testowania i debugowania mojego kodu. Próbowałem enkapsulować wszystkie polecenia hosta zapisu za pomocą {}, ale nadal pokazywał cały kod, ponieważ po uruchomieniu skryptu widzę wszystkie polecenia pokazane w oknie PS. Gdyby to była partia, po prostu wyłączałbym @echo, ale nie jestem pewien, jak to zrobić w PowerShell.

Drugi problem to bałagan. Działa, ale jest bałagan. Czuję, że powinienem być w stanie trochę skondensować ten kod, ale nie jestem pewien, jak to zrobić. W takim przypadku muszę iterować każdą aplikację, a następnie zainstalować inny program, jeśli ta konkretna „aplikacja” nie zostanie znaleziona w systemie. Poszukuje Google Earth, a jeśli go nie znajdzie, uruchamia polecenie, aby go zainstalować ... następnie powraca do następnej aplikacji ... nie znajduje go i uruchamia inne polecenie, aby zainstalować ten i tak dalej. Trafiłem jednak tym murem. Szukałem pętli i tablic, aby sprawdzić, czy mogę znaleźć lepszy sposób, niż wymienianie każdego polecenia w kółko, tak jak tutaj, ale wydaje się, że dla pętli i dla każdej pętli są przeznaczone zrobić to samo dla każdego elementu w tablicy.

Wszelkie wskazówki i porady będą mile widziane. Z góry dziękuję.

Mike Van Dunk
źródło

Odpowiedzi:

0

1- Nawiasy klamrowe.

Jest ich wiele niepotrzebnych par. Sprawdź różnicę, jaką mogą zrobić:

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

Powtarzanie if/elsei Get-ItemPropertysekcje kodu są zawsze owinięte razem w parę nawiasów klamrowych. Usuń te dodatkowe pary. Są przyczyną pojawienia się kodu w wynikach konsoli. Wygląda na to, że są wynikiem wklejenia zawartości funkcji od początku skryptu. Więcej informacji na temat nawiasów klamrowych / nawiasów / nawiasów: http://www.powertheshell.com/scriptblock/

2- To jest subiektywne.

Ogólna wskazówka: popraw spójność wcięcia. Rozwiązywanie problemów z kodem może być trudne. Rozwiązywanie problemów z nieporządnym kodem jest trudniejsze. Ułatw sobie, poprawiając czytelność podczas pisania.

Na przykład dodatkowe nawiasy klamrowe z bloku kodu, o którym wspomniałem, odpowiadając na twoje pierwsze pytanie, można byłoby łatwiej zidentyfikować, gdyby wcięcia były utrzymywane bardziej konsekwentnie.

Oryginalny blok (zredagowany):

{
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\*'
    )
}
...
}

Tym razem z ulepszonym wcięciem:

{
    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\*'
        )
    }
    ...
}

Największym czynnikiem przyczyniającym się do bałaganu w tym skrypcie jest wielokrotne użycie tego bloku kodu. Skrypt zaczyna się od funkcji. To świetnie, ponieważ będziesz wywoływał zawartość funkcji wiele razy. Do tego służą funkcje. Jednak zawartość funkcji jest wielokrotnie wywoływana w całym skrypcie. Nie ma potrzeby ciągłego ustawiania $regpathzmiennej i wyszukiwania jej Get-ItemProperty. To właśnie robi twoja funkcja. Usunięcie tych bloków powoduje przecięcie kodu na pół.

Masz rację - pętle i tablice pomogłyby Twojemu kodowi na wiele sposobów. Zachowując swoją funkcję, możesz przeglądać każdą aplikację za pomocą czegoś takiego:

# 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)
    }

}
korzeń
źródło