web-dev-qa-db-ja.com

PowerShellZipファイルを同期的に

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)

    }
}

このスニペットは実際にはフォルダーを圧縮しますが、非同期の方法です。実際、Shell.ApplicationオブジェクトのCopyHereメソッドは圧縮を開始し、その完了を待ちません。次に、スクリプトの次のステートメントが混乱します(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
10
Steve B

私はついに、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
5
Steve B

手動で一時停止すると正常に機能したため、「適切な」解決策が見つかるまで使用できる一時的なハックを次に示します。一般に、このような「遅延」と「タイマー」を使用することは、ミッションクリティカルなことに対して行うことではありません。そうは言っても、より良い答えが見つかるまで、これを実行して、それが機能するかどうかを確認できます。

  • プロセスを手動で数回実行し、通常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%の確率で問題なく動作することが証明されていれば、十分かもしれません。

1
Kerry