PowerShell zipファイルを同期的に


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)

    }
}

このスニペットは実際にはフォルダーを圧縮しますが、非同期に圧縮します。実際、Shell.ApplicationオブジェクトのCopyHereメソッドは圧縮を開始し、その完了を待ちません。スクリプトの次のステートメントは混乱します(zipファイルのプロセスが完了していないため)。

助言がありますか?可能であれば、実行可能ファイルの追加を避け、純粋なWindows機能を維持したいと思います。

[編集]私のPS1ファイルの完全なコンテンツからDBの実際の名前を除いたもの。スクリプトの目的は、一連の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

同期している場合に機能するかどうかを確認するだけです。foreachの後に次のコードを追加すると、期待どおりに機能しますか?Line1: Write-Host "Press any key to continue ..." Line2:$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Kerry

また、テスト中は、zipプロセスが完了したことを手動で確認するまで「続行するには任意のキーを押さない」ことを想定しています。
ケリー

@ケリー:手動テストを追加すると、実際に期待どおりに動作します
スティーブB

回答:


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オブジェクトを操作できるため、ここで動作を問題なく再現できるはずです
Steve B

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%の時間で問題なく動作する場合は、それで十分です。


ようやく解決策を見つけました。とにかくあなたの助けに感謝します。Thx
スティーブB
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.