arquivo zip PowerShell de forma síncrona

10

Em um script do PowerShell, quero compactar uma pasta antes de excluir a pasta. Eu corro o seguinte (não me lembro onde encontrei o trecho):

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)

    }
}

Esse snippet na verdade comprime a pasta, mas de maneira assíncrona. Na verdade, o método CopyHere dos objetos Shell.Application inicia a compactação e não espera sua conclusão. As próximas declarações dos meus scripts, então, atrapalham (já que o processo do arquivo zip não está completo).

Alguma sugestão? Se possível, gostaria de evitar adicionar arquivos executáveis e manter os recursos do Windows.

[edit] conteúdo completo do meu arquivo PS1 menos o nome real do DB. O objetivo do script é fazer backup de um conjunto de db SQL e, em seguida, compactar os backups em um único pacote em uma pasta nomeada com a data atual:

$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
    
por Steve B 30.05.2011 / 10:09

2 respostas

5

Eu finalmente encontrei uma maneira limpa, brincando com as propriedades dos objetos com. Especialmente, o snippet a seguir pode testar se o arquivo está presente no arquivo 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
    }
}

O script completo é o seguinte:

$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
    
por 31.05.2011 / 14:13
1

Como funcionou bem quando você pausou manualmente, aqui está um hack temporário que você pode usar até que a solução "certa" seja encontrada. Geralmente usando "atrasos" e "temporizadores" como este não é o que você faria para as coisas de missão crítica. Dito isso, até encontrar uma resposta melhor, você pode fazer isso e ver se funciona:

  • Faça o processo manualmente algumas vezes e HORA em quanto tempo, em segundos, o processo zip normalmente é concluído. Se o tamanho do banco de dados for geralmente o mesmo todos os dias, o tempo necessário para concluir provavelmente será a média mais ou menos na mesma época.

  • Digamos que você obtenha uma média de 60 segundos em seus testes manuais. Seja conservador e multiplique por 4 ou mais, pois provavelmente não levará 4 vezes mais tempo do que o normal em dias "normais". Então agora você tem 240 segundos (60 segundos de tempo médio 4).

  • Então, por enquanto, em vez de ter o código "pressione qualquer tecla para continuar", substitua-o por um DELAY no código para que o script espere um pouco para que o zip termine . Isso requer alguns ajustes e adivinhações sobre horários e não é uma boa abordagem. Mas em uma pitada ...

  • De qualquer forma, se você quiser experimentar, altere o código para:

Se estiver usando o PowerShell V1:

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

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

Se estiver usando o PowerShell V2, use o cmdlet do sono:

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

Start-Sleep -Second 240

Para mexer com tempos em V1, usa milissegundos. (Então 10 segundos = 10000)

Para mexer com tempos em V2, usa segundos. (240 = 240 segundos)

Eu nunca usaria isso na produção, mas se não for grande coisa e ela funcionar muito bem 99% do tempo, pode ser bom o suficiente.

    
por 31.05.2011 / 11:29