Поштовий файл PowerShell синхронно


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 об'єктів Shell.Application запускає стиснення і не чекає його завершення. Наступні твердження моїх сценаріїв потім псуються (оскільки процес zip-файлу не завершений).

Будь-які пропозиції? Якщо можливо, я хотів би уникати додавання файлів, що виконуються, та залишатись у чистому режимі Windows.

[редагувати] повний вміст мого файлу PS1 за вирахуванням фактичної назви БД. Мета скрипту - створити резервну копію набору db SQL, а потім зафіксувати резервні копії в одному пакеті в папці, названій з поточною датою:

$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

Просто, щоб побачити, чи працював би він, якщо він був синхронним: Якщо ви додасте наступний код після того, як ваш foreach, чи він працює, як очікувалося? Line1: Write-Host "Press any key to continue ..." Line2:$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Керрі

І під час тесту я, очевидно, припускаю, що ви не будете "натискати жодну клавішу для продовження", поки вручну не підтвердите, що процес поштового завершення завершено.
Керрі

@Kerry: насправді це працює так, як очікувалося, коли я додаю ваш ручний тест
Стів Б

Відповіді:


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

як можна використовувати його на VB?
MacGyver

@Leandro: ти маєш на увазі сценарій VB? Оскільки PowerShell та VBScript - це сценарій мови, здатний працювати з COM-об’єктами, ви повинні мати можливість повторити свою поведінку тут без особливих труднощів
Стів Б

1

Оскільки він справно працював, коли ви його вручну призупинили, ось тимчасовий злом, який ви можете використовувати, поки не буде знайдено "правильне" рішення. Як правило, використання таких «затримок» та «таймерів», як це НЕ, ви б зробили для критичних речей. Однак, поки не знайдеться кращої відповіді, ви можете зробити це і побачити, чи працює він:

  • Виконайте процес вручну кілька разів, і ЧАС, скільки часу в секундах, як правило, триває процес поштового зв’язку. Якщо розмір бази даних, як правило, однаковий щодня, то час, необхідний для його закінчення, буде, ймовірно, приблизно в один і той же час.

  • Скажімо, ви отримуєте в середньому 60 секунд у ваших ручних тестах. Будьте консервативні і помножте його на 4 або близько того, що, швидше за все, це не займе 4 рази довше, ніж зазвичай, у "звичайні" дні. Отже, у вас є 240 секунд (60 секунд в середньому 4 рази).

  • Тому зараз замість того, щоб у коді був "натисніть будь-яку клавішу для продовження", замініть його на ЗАЛИШЕННЯ в коді, щоб сценарій трохи розвісився, щоб зачекати, коли блискавка закінчиться. Для цього потрібні певні налаштування та здогадки про терміни, і це не дуже підходящий підхід. Але на дрібку ...

  • У будь-якому випадку, якщо ви хочете спробувати, змініть код на:

Якщо ви використовуєте 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% часу, це може бути досить добре.


Я нарешті знайшов рішення. Я вдячний за вашу допомогу в будь-якому випадку. Thx
Стів Б
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.