PowerShell-Zip-Datei synchron


10

In einem PowerShell-Skript möchte ich einen Ordner komprimieren, bevor ich den Ordner lösche. Ich führe Folgendes aus (ich erinnere mich nicht, wo ich das Snippet gefunden habe):

function Compress-ToZip
{
    param([string]$zipfilename)

    if(-not (test-path($zipfilename)))
    {
        set-content $zipfilename ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
        (Get-ChildItem $zipfilename).IsReadOnly = $false   
    }

    $shellApplication = new-object -com shell.application
    $zipPackage = $shellApplication.NameSpace($zipfilename)

    foreach($file in $input)
    {
         $zipPackage.CopyHere($file.FullName)

    }
}

Dieses Snippet komprimiert den Ordner tatsächlich, jedoch auf asynchrone Weise. Tatsächlich startet die CopyHere-Methode der Shell.Application-Objekte die Komprimierung und wartet nicht auf ihren Abschluss. Die nächsten Anweisungen meiner Skripte sind dann durcheinander (da der Zip-Datei-Prozess nicht abgeschlossen ist).

Irgendwelche Vorschläge? Wenn möglich, möchte ich das Hinzufügen ausführbarer Dateien vermeiden und mich auf reine Windows-Funktionen beschränken.

[Bearbeiten] Vollständiger Inhalt meiner PS1-Datei abzüglich des tatsächlichen Namens der Datenbank. Das Ziel des Skripts besteht darin, eine Reihe von SQL-Datenbanken zu sichern und dann die Sicherungen in einem einzelnen Paket in einem Ordner mit dem aktuellen Datum zu komprimieren:

$VerbosePreferenceBak = $VerbosePreference
$VerbosePreference = "Continue"

add-PSSnapin SqlServerCmdletSnapin100

function BackupDB([string] $dbName, [string] $outDir)
{
    Write-Host "Backup de la base :  $dbName"
    $script = "BACKUP DATABASE $dbName TO DISK = '$outDir\$dbName.bak' WITH FORMAT, COPY_ONLY;"

    Invoke-Sqlcmd -Query "$script" -ServerInstance "." -QueryTimeOut 600
    Write-Host "Ok !"
}

function Compress-ToZip
{
    param([string]$zipfilename)

Write-Host "Compression du dossier"

    if(-not (test-path($zipfilename)))
    {
        set-content $zipfilename ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
        (Get-ChildItem $zipfilename).IsReadOnly = $false   
    }

    $shellApplication = new-object -com shell.application
    $zipPackage = $shellApplication.NameSpace($zipfilename)

    foreach($file in $input)
    {
         $zipPackage.CopyHere($file.FullName)       
    }
Write-Host "Press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

}


$targetDir = "E:\Backup SQL"
$date = Get-Date -format "yyyy-MM-dd"
$newDir = New-Item -ItemType Directory "$targetDir\$date\sql" -Force

BackupDB  "database 1" "$newDir"
BackupDB  "database 2" "$newDir"
BackupDB  "database 3" "$newDir"

Get-Item $newDir | Compress-ToZip "$targetDir\$date\sql_$date.zip"


Write-Host "."
remove-item $newDir -Force -Confirm:$false -Recurse

$VerbosePreference = $VerbosePreferenceBak

Nur um zu sehen, ob es funktioniert, wenn es synchron ist: Wenn Sie nach Ihrem foreach den folgenden Code hinzufügen, funktioniert es wie erwartet? Linie 1: Write-Host "Press any key to continue ..." Linie 2:$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Kerry

Und während des Tests gehe ich offensichtlich davon aus, dass Sie keine Taste drücken, um fortzufahren, bis Sie manuell bestätigen, dass der Zip-Vorgang abgeschlossen ist.
Kerry

@ Kerry: Es funktioniert tatsächlich wie erwartet, wenn ich Ihren manuellen Test hinzufüge
Steve B

Antworten:


5

Ich habe endlich einen sauberen Weg gefunden und mit den Eigenschaften der COM-Objekte gespielt. Insbesondere kann das folgende Snippet testen, ob die Datei in der Zip-Datei vorhanden ist:

foreach($file in $input)
{
    $zipPackage.CopyHere($file.FullName)    
    $size = $zipPackage.Items().Item($file.Name).Size
    while($zipPackage.Items().Item($file.Name) -Eq $null)
    {
        start-sleep -seconds 1
        write-host "." -nonewline
    }
}

Das vollständige Skript lautet wie folgt:

$VerbosePreferenceBak = $VerbosePreference
$VerbosePreference = "Continue"

add-PSSnapin SqlServerCmdletSnapin100

function BackupDB([string] $dbName, [string] $outDir) {
    Write-Host "Backup de la base :  $dbName"
    $script = "BACKUP DATABASE $dbName TO DISK = '$outDir\$dbName.bak' WITH FORMAT, COPY_ONLY;"

    Invoke-Sqlcmd -Query "$script" -ServerInstance "." -QueryTimeOut 600
    Write-Host "Ok !"
}

function Compress-ToZip {
    param([string]$zipfilename)

    Write-Host "Compression du dossier"

    if(-not (test-path($zipfilename)))  {
        set-content $zipfilename ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
        (Get-ChildItem $zipfilename).IsReadOnly = $false   
    }

    $shellApplication = new-object -com shell.application
    $zipPackage = $shellApplication.NameSpace($zipfilename)

    foreach($file in $input) {
        $zipPackage.CopyHere($file.FullName)    
        $size = $zipPackage.Items().Item($file.Name).Size
        while($zipPackage.Items().Item($file.Name) -Eq $null)
        {
            start-sleep -seconds 1
            write-host "." -nonewline
        }
        write-host "."
    }      
}


$targetDir = "E:\Backup SQL"
$date = Get-Date -format "yyyy-MM-dd"
$newDir = New-Item -ItemType Directory "$targetDir\$date\sql" -Force

BackupDB  "DB1" "$newDir"
BackupDB  "DB2" "$newDir"
BackupDB  "DB3" "$newDir"
BackupDB  "DB4" "$newDir"

Get-ChildItem "$newDir" | Compress-ToZip "$targetDir\$date\sql_$date.zip"

remove-item $newDir -Force -Confirm:$false -Recurse

$VerbosePreference = $VerbosePreferenceBak

Wie können Sie es auf VB verwenden?
MacGyver

@Leandro: Meinst du VB-Skript? Da sowohl PowerShell als auch VBScript eine Skriptsprache sind, die mit COM-Objekten arbeiten kann, sollten Sie in der Lage sein, das Verhalten hier ohne große Schwierigkeiten zu replizieren
Steve B

1

Da es gut funktioniert hat, als Sie es manuell angehalten haben, ist hier ein vorübergehender Hack, den Sie möglicherweise verwenden können, bis die "richtige" Lösung gefunden ist. Im Allgemeinen ist die Verwendung von "Verzögerungen" und "Timern" wie diesen NICHT das, was Sie für geschäftskritische Dinge tun würden. Das heißt, bis eine bessere Antwort gefunden wird, können Sie dies tun und sehen, ob es funktioniert:

  • Führen Sie den Vorgang einige Male manuell durch und geben Sie an, wie lange es in Sekunden dauert, bis der Zip-Vorgang abgeschlossen ist. Wenn die Datenbankgröße im Allgemeinen jeden Tag gleich ist, wird die durchschnittliche Zeit bis zur Fertigstellung wahrscheinlich ungefähr zur gleichen Zeit betragen.

  • Angenommen, Sie erhalten in Ihren manuellen Tests durchschnittlich 60 Sekunden. Seien Sie konservativ und multiplizieren Sie es mit 4 oder so, da es an "normalen" Tagen wahrscheinlich nicht 4-mal länger dauert als gewöhnlich. Jetzt haben Sie also 240 Sekunden (durchschnittlich 60 Sekunden mal 4).

  • Anstatt den Code "Drücken Sie eine beliebige Taste, um fortzufahren" dort zu haben, ersetzen Sie ihn vorerst durch eine VERZÖGERUNG im Code, damit das Skript nur eine Weile hängen bleibt, bis die Zip-Datei fertig ist. Dies erfordert einige Anpassungen und Schätzungen des Timings und ist kein guter Ansatz. Aber zur Not ...

  • Wenn Sie es versuchen möchten, ändern Sie den Code in:

Bei Verwendung von PowerShell V1:

foreach($file in $input)
{
  $zipPackage.CopyHere($file.FullName)       
}

[System.Threading.Thread]::Sleep(240000)

Wenn Sie PowerShell V2 verwenden, verwenden Sie stattdessen das Cmdlet Sleep:

foreach($file in $input)
{
   $zipPackage.CopyHere($file.FullName)       
}

Start-Sleep -Second 240

Um mit den Zeiten in V1 zu spielen, werden Millisekunden verwendet. (Also 10 Sekunden = 10000)

Um mit den Zeiten in V2 zu spielen, werden Sekunden benötigt. (240 = 240 Sekunden)

Ich würde dies niemals in der Produktion verwenden, aber wenn es keine so große Sache ist und es sich in 99% der Fälle als gut funktioniert, könnte es gut genug sein.


Ich habe endlich die Lösung gefunden. Ich freue mich trotzdem über Ihre Hilfe. Thx
Steve B
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.