10

В сценарии PowerShell я хочу сжать папку перед ее удалением. Я запускаю следующее (я не помню, где я нашел фрагмент):

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)

    }
}

Этот фрагмент фактически сжимает папку, но асинхронно. На самом деле, метод CopyHere в командной консоли.Объект приложения запускает сжатие и не ждет его завершения. Следующие операторы моих скриптов затем запутываются (так как процесс zip-файла не завершен).

Какие-либо предложения? Если возможно, я бы хотел не добавлять исполняемые файлы и использовать только функции Windows.

[править] полное содержимое моего файла PS1 минус фактическое имя БД. Целью сценария является резервное копирование набора SQL db, а затем архивирование резервных копий в один пакет в папке с текущей датой:

$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

2 ответа2

5

Я наконец нашел чистый способ, поиграв со свойствами com-объектов. В частности, следующий фрагмент может проверить, присутствует ли файл в 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
    }
}

Полный скрипт выглядит следующим образом:

$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
1

Поскольку он работал нормально, когда вы вручную ставили его на паузу, вот временный хак, который вы можете использовать, пока не найдете "правильное" решение. Обычно использование таких "задержек" и "таймеров" НЕ является тем, что вы бы сделали для критически важных задач. Тем не менее, пока не будет найден лучший ответ, вы можете сделать это и посмотреть, работает ли он:

  • Выполните процесс вручную несколько раз и ВРЕМЯ, сколько времени в секундах обычно занимает процесс zip для завершения. Если размер базы данных, как правило, один и тот же каждый день, то время, необходимое для ее завершения, вероятно, будет в среднем примерно одинаковым.

  • Допустим, вы получаете в среднем 60 секунд в ваших ручных тестах. Будьте консервативны и умножьте это на 4 или около того, так как это не займет в 4 раза больше времени, чем обычно в "нормальные" дни. Так что теперь у вас есть 240 секунд (60 секунд в среднем 4 раза).

  • Так что сейчас вместо того, чтобы иметь код "нажмите любую клавишу для продолжения", замените его на DELAY в коде, чтобы скрипт просто зависал немного, ожидая окончания работы zip. Это требует некоторой подстройки и догадок по времени и не является хорошим подходом. Но в крайнем случае ...

  • В любом случае, если вы хотите попробовать, измените код на:

При использовании PowerShell V1:

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

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

При использовании PowerShell V2 используйте вместо этого командлет Sleep:

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

Start-Sleep -Second 240

Чтобы связываться со временем в V1, он использует миллисекунды. (Итак 10 секунд = 10000)

Чтобы возиться со временем в V2 он использует секунды. (240 = 240 секунд)

Я бы никогда не использовал это в производстве, но если это не так уж важно, и это хорошо работает в 99% случаев, это может быть достаточно.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .