File zip di PowerShell in modo sincrono


10

In uno script di PowerShell, voglio comprimere una cartella prima di eliminare la cartella. Corro il seguente (non ricordo dove ho trovato lo snippet):

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)

    }
}

Questo frammento in realtà comprime la cartella, ma in modo asincrono. In effetti, il metodo CopyHere degli oggetti Shell.Application avvia la compressione e non attende il suo completamento. Le successive dichiarazioni dei miei script quindi incasinano (poiché il processo del file zip non è completato).

Eventuali suggerimenti? Se possibile, vorrei evitare di aggiungere file eseguibili e rimanere sulle funzionalità di Windows puro.

[modifica] contenuto completo del mio file PS1 meno il nome effettivo del DB. L'obiettivo dello script è di eseguire il backup di un set di db SQL, quindi comprimere i backup in un singolo pacchetto in una cartella denominata con la data corrente:

$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

Solo per vedere se funzionerebbe se fosse sincrono: se aggiungi il seguente codice dopo la tua ricerca, funziona come previsto? Riga1: Write-Host "Press any key to continue ..." Riga2:$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Kerry

E durante il test presumo ovviamente che non "premi alcun tasto per continuare" fino a quando non confermerai manualmente che il processo zip è stato completato.
Kerry,

@Kerry: funziona davvero come previsto quando aggiungo il test manuale
Steve B

Risposte:


5

Alla fine ho trovato un modo pulito, giocando con le proprietà degli oggetti com. In particolare, il frammento seguente può verificare se il file è presente nel file zip:

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

Lo script completo è il seguente:

$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

come puoi usarlo su VB?
MacGyver,

@Leandro: intendi lo script VB? Poiché sia ​​PowerShell che VBScript sono linguaggi di scripting, in grado di funzionare con oggetti COM, dovresti essere in grado di replicare il comportamento qui senza troppe difficoltà
Steve B,

1

Poiché ha funzionato bene quando lo hai messo in pausa manualmente, ecco un trucco temporaneo che potresti essere in grado di utilizzare fino a trovare la soluzione "giusta". Generalmente usare "ritardi" e "timer" come questo NON è quello che faresti per le cose mission-critical. Detto questo, fino a quando non viene trovata una risposta migliore, puoi farlo e vedere se funziona:

  • Esegui il processo manualmente un paio di volte e TIME quanto tempo in secondi di solito richiede il completamento del processo zip. Se la dimensione del database è generalmente la stessa ogni giorno, probabilmente il tempo necessario per terminare sarà nella media alla stessa ora.

  • Supponiamo che tu abbia una media di 60 secondi nei test manuali. Sii prudente e moltiplicalo per 4 o giù di lì perché probabilmente non ci vorrà 4 volte più del solito nei giorni "normali". Quindi ora hai 240 secondi (60 secondi in media 4 volte).

  • Quindi per ora, invece di avere il codice "premi un tasto qualsiasi per continuare", sostituiscilo con un RITARDO nel codice per fare in modo che lo script si blocchi per un po 'in attesa che finisca lo zip. Ciò richiede alcune modifiche e indovinazioni sui tempi e non è un buon approccio. Ma in un pizzico ...

  • Ad ogni modo, se vuoi provarlo, cambia il codice in:

Se si utilizza PowerShell V1:

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

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

Se si utilizza PowerShell V2, utilizzare invece il cmdlet Sleep:

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

Start-Sleep -Second 240

Per scherzare con i tempi in V1 utilizza millisecondi. (Quindi 10 secondi = 10000)

Per scherzare con i tempi in V2 utilizza secondi. (240 = 240 secondi)

Non lo userei mai in produzione, ma se non è un grosso problema, e si dimostra che funzioni bene nel 99% dei casi, potrebbe essere abbastanza buono.


Finalmente ho trovato la soluzione. Apprezzo comunque il tuo aiuto. Grazie
Steve B,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.