PowerShell 同步压缩文件

PowerShell 同步压缩文件

在 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 文件的完整内容减去数据库的实际名称。该脚本的目标是备份一组 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

答案1

我终于找到了一种干净的方法,利用 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

答案2

因为手动暂停时它工作正常,所以这里有一个临时的破解方法,你可以在找到“正确”的解决方案之前使用它。通常,像这样使用“延迟”和“计时器”并不是你对关键任务所做的事情。也就是说,在找到更好的答案之前,你可以这样做,看看它是否有效:

  • 手动执行该过程几次,并计算压缩过程通常需要多长时间(以秒为单位)。如果数据库大小通常每天都相同,那么完成所需的时间可能平均大约相同。

  • 假设您的手动测试平均耗时 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 cmdlet:

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

Start-Sleep -Second 240

为了处理 V1 中的时间,它使用毫秒。(因此 10 秒 = 10000)

为了处理 V2 中的时间,它使用秒。(240 = 240 秒)

我永远不会在生产中使用它,但如果这不是什么大问题,并且 99% 的时间都能正常工作,那么它可能就足够了。

相关内容