Fichier zip PowerShell de manière synchrone

10

Dans un script PowerShell, je souhaite compresser un dossier avant de supprimer le dossier. Je lance ce qui suit (je ne me souviens pas où j'ai trouvé l'extrait):

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)

    }
}

Cet extrait compresse en fait le dossier, mais de manière asynchrone. En fait, la méthode CopyHere des objets Shell.Application démarre la compression et n'attend pas son achèvement. Les déclarations suivantes de mes scripts se gâchent ensuite (car le processus de fichier zip n'est pas terminé).

Aucune suggestion? Si possible, je voudrais éviter d'ajouter des fichiers exécutables et rester sur des fonctionnalités Windows pures.

[modifier] le contenu complet de mon fichier PS1 moins le nom réel de la base de données. Le but du script est de sauvegarder un ensemble de bases de données SQL, puis de compresser les sauvegardes dans un seul package dans un dossier nommé avec la date actuelle:

$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
Steve B
la source
Juste pour voir si cela fonctionnerait s'il était synchrone: Si vous ajoutez le code suivant après votre foreach, cela fonctionne-t-il comme prévu? Write-Host "Press any key to continue ..." Ligne 1 : Ligne 2:$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Kerry
Et pendant le test, je suppose évidemment que vous "n'appuyez sur aucune touche pour continuer" jusqu'à ce que vous confirmiez manuellement que le processus zip est terminé.
Kerry
@Kerry: cela fonctionne comme prévu lorsque j'ajoute votre test manuel
Steve B

Réponses:

5

J'ai finalement trouvé un moyen propre, en jouant avec les propriétés des objets com. En particulier, l'extrait de code suivant peut tester si le fichier est présent dans le fichier 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
    }
}

Le script complet est le suivant:

$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
Steve B
la source
comment pouvez-vous l'utiliser sur VB?
MacGyver
@Leandro: vous voulez dire le script VB? Comme PowerShell et VBScript sont des langages de script, capables de travailler avec des objets COM, vous devriez être en mesure de reproduire le comportement ici sans trop de difficultés
Steve B
1

Parce que cela a bien fonctionné lorsque vous l'avez interrompu manuellement, voici un hack temporaire que vous pourrez utiliser jusqu'à ce que la "bonne" solution soit trouvée. Généralement, utiliser des «retards» et des «minuteries» comme celui-ci n'est PAS ce que vous feriez pour des choses essentielles à la mission. Cela dit, jusqu'à ce qu'une meilleure réponse soit trouvée, vous pouvez le faire et voir si cela fonctionne:

  • Effectuez le processus manuellement plusieurs fois et TEMPS combien de temps en secondes il faut généralement pour terminer le processus zip. Si la taille de la base de données est généralement la même tous les jours, le temps nécessaire pour terminer sera probablement moyen autour de la même heure.

  • Disons que vous obtenez en moyenne 60 secondes dans vos tests manuels. Soyez prudent et multipliez-le par 4 environ, car cela ne prendra probablement pas 4 fois plus longtemps que d'habitude les jours "normaux". Alors maintenant, vous avez 240 secondes (60 secondes en moyenne 4 fois).

  • Donc pour l'instant, au lieu d'avoir le code "appuyez sur n'importe quelle touche pour continuer", remplacez-le par un DELAY dans le code pour que le script se bloque un peu en attendant que le zip se termine. Cela nécessite quelques ajustements et devinettes sur les horaires et n'est pas une bonne approche. Mais à la rigueur ...

  • Quoi qu'il en soit, si vous voulez l'essayer, changez le code en:

Si vous utilisez PowerShell V1:

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

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

Si vous utilisez PowerShell V2, utilisez plutôt l'applet de commande Sleep:

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

Start-Sleep -Second 240

Pour jouer avec les temps dans V1, il utilise des millisecondes. (Donc 10 secondes = 10000)

Pour jouer avec les temps dans V2, il utilise des secondes. (240 = 240 secondes)

Je ne l'utiliserais jamais en production, mais si ce n'est pas si grave, et que cela fonctionne très bien 99% du temps, cela pourrait être suffisant.

Kerry
la source
J'ai finalement trouvé la solution. J'apprécie quand même votre aide. Thx
Steve B