En un script de PowerShell, quiero comprimir una carpeta antes de eliminarla. Ejecuto lo siguiente (no recuerdo dónde encontré el fragmento):
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)
}
}
Este fragmento realmente comprime la carpeta, pero de forma asincrónica. De hecho, el método CopyHere de los objetos Shell.Application inicia la compresión y no espera su finalización. Las siguientes declaraciones de mis scripts luego se confunden (ya que el proceso del archivo zip no se completa).
¿Alguna sugerencia? Si es posible, me gustaría evitar agregar archivos ejecutables y mantener las características puras de Windows.
[editar] contenido completo de mi archivo PS1 menos el nombre real de la base de datos. El objetivo del script es hacer una copia de seguridad de un conjunto de SQL db, luego comprimir las copias de seguridad en un solo paquete en una carpeta con la fecha actual:
$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
fuente
Write-Host "Press any key to continue ..."
Línea2:$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Respuestas:
Finalmente encontré una manera limpia, jugando con las propiedades de los objetos com. Especialmente, el siguiente fragmento puede probar si el archivo está presente en el archivo zip:
El guión completo es el siguiente:
fuente
Debido a que funcionó bien cuando lo detuvo manualmente, aquí hay un truco temporal que puede usar hasta que encuentre la solución "correcta". En general, el uso de "retrasos" y "temporizadores" como este NO es lo que haría para las cosas de misión crítica. Dicho esto, hasta que se encuentre una mejor respuesta, puede hacer esto y ver si funciona:
Realice el proceso manualmente varias veces y TIEMPO cuánto tiempo en segundos generalmente tarda en completarse el proceso zip. Si el tamaño de la base de datos es generalmente el mismo todos los días, entonces el tiempo que demora en terminar probablemente sea el promedio de la misma hora.
Digamos que obtiene un promedio de 60 segundos en sus pruebas manuales. Sea conservador y multiplíquelo por 4 más o menos, ya que es probable que no tome 4 veces más de lo habitual en días "normales". Entonces ahora tienes 240 segundos (60 segundos promedio 4).
Entonces, por ahora, en lugar de tener el código "presione cualquier tecla para continuar" allí, reemplácelo con un RETARDO en el código para que el script se cuelgue un poco y espere a que termine el zip. Esto requiere algunos ajustes y conjeturas sobre los tiempos y no es un buen enfoque. Pero en un apuro ...
De todos modos, si quieres probarlo, cambia el código a:
Si usa PowerShell V1:
Si usa PowerShell V2, use el cmdlet Sleep en su lugar:
Para meterse con los tiempos en V1, usa milisegundos. (Entonces 10 segundos = 10000)
Para meterse con los tiempos en V2, usa segundos. (240 = 240 segundos)
Nunca usaría esto en producción, pero si no es un gran problema y demuestra que funciona bien el 99% del tiempo, podría ser lo suficientemente bueno.
fuente