File zip PowerShell secara sinkron

10

Dalam skrip PowerShell, saya ingin zip folder sebelum menghapus folder. Saya menjalankan yang berikut (Saya tidak ingat di mana saya menemukan potongannya):

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)

    }
}

Cuplikan ini sebenarnya memampatkan folder, tetapi dengan cara yang tidak sinkron. Bahkan, metode CopyHere dari objek Shell.Application memulai kompresi dan tidak menunggu penyelesaiannya. Pernyataan skrip saya selanjutnya berantakan (karena proses file zip tidak selesai).

Ada saran? Jika memungkinkan saya ingin menghindari menambahkan file yang dapat dieksekusi dan tetap menggunakan fitur Windows murni.

[sunting] konten penuh file PS1 saya minus nama sebenarnya dari DB. Tujuan skrip adalah untuk mencadangkan sekumpulan SQL db, lalu meng-zip cadangan dalam satu paket dalam folder bernama dengan tanggal saat ini:

$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
Steve B
sumber
Hanya untuk melihat apakah itu akan berfungsi jika itu sinkron: Jika Anda menambahkan kode berikut setelah foreach Anda bekerja seperti yang diharapkan? Baris1: Write-Host "Press any key to continue ..." Baris2:$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Kerry
Dan selama pengujian saya jelas berasumsi bahwa Anda tidak akan "menekan tombol apa saja untuk melanjutkan" sampai Anda secara manual mengkonfirmasi bahwa proses zip telah selesai.
Kerry
@ Kerry: ini benar-benar berfungsi seperti yang diharapkan ketika saya menambahkan tes manual Anda
Steve B

Jawaban:

5

Saya akhirnya menemukan cara yang bersih, bermain dengan properti dari objek com. Khususnya, cuplikan berikut ini dapat menguji apakah file tersebut ada di file 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
    }
}

Script lengkapnya adalah sebagai berikut:

$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
Steve B
sumber
bagaimana Anda bisa menggunakannya di VB?
MacGyver
@Leandro: maksud Anda skrip VB? Karena baik PowerShell dan VBScript adalah bahasa scripting, dapat bekerja dengan objek COM, Anda harus dapat mereplikasi perilaku di sini tanpa banyak kesulitan
Steve B
1

Karena itu berfungsi dengan baik ketika Anda menjeda secara manual, berikut ini adalah peretasan sementara yang mungkin dapat Anda gunakan sampai solusi "benar" ditemukan. Umumnya menggunakan "penundaan" dan "timer" seperti ini BUKAN apa yang akan Anda lakukan untuk hal-hal penting misi. Yang mengatakan, sampai jawaban yang lebih baik ditemukan, Anda dapat melakukan ini dan melihat apakah itu berhasil:

  • Lakukan proses secara manual beberapa kali dan TIME berapa lama dalam hitungan detik biasanya proses zip selesai. Jika ukuran basis data umumnya sama setiap hari maka waktu yang dibutuhkan untuk menyelesaikannya mungkin akan rata-rata pada waktu yang sama.

  • Katakanlah Anda mendapatkan rata-rata 60 detik dalam tes manual Anda. Bersikaplah konservatif dan kalikan dengan 4 atau lebih karena kemungkinan tidak akan memakan waktu 4 kali lebih lama dari biasanya pada hari "normal". Jadi sekarang Anda memiliki 240 detik (rata-rata 60 detik kali 4).

  • Jadi untuk saat ini, alih-alih memiliki kode "tekan tombol apa saja untuk melanjutkan" di sana, ganti kode itu dengan DELAY dalam kode tersebut agar skripnya nongkrong sebentar untuk menunggu zip selesai. Ini membutuhkan beberapa penyesuaian dan perkiraan waktu dan bukan pendekatan yang baik. Tapi dalam keadaan darurat ...

  • Bagaimanapun, jika Anda ingin mencobanya, ubah kode menjadi:

Jika menggunakan PowerShell V1:

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

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

Jika menggunakan PowerShell V2, gunakan cmdlet Tidur sebagai gantinya:

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

Start-Sleep -Second 240

Untuk mengacaukan waktu di V1 menggunakan milidetik. (Jadi 10 detik = 10.000)

Untuk mengacaukan waktu di V2 menggunakan detik. (240 = 240 detik)

Saya tidak akan pernah menggunakan ini dalam produksi tetapi jika itu bukan masalah besar, dan terbukti bekerja dengan baik 99% dari waktu, mungkin cukup baik.

Kerry
sumber
Saya akhirnya menemukan solusinya. Saya menghargai bantuan Anda. Thx
Steve B