W skrypcie PowerShell chcę spakować folder przed jego usunięciem. Uruchamiam następujące (nie pamiętam, gdzie znalazłem fragment):
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)
}
}
Ten fragment kodu kompresuje folder, ale w sposób asynchroniczny. W rzeczywistości metoda CopyHere obiektów Shell.Application rozpoczyna kompresję i nie czeka na jej zakończenie. Następne instrukcje moich skryptów psują się (ponieważ plik zip nie został zakończony).
Jakieś sugestie? Jeśli to możliwe, chciałbym uniknąć dodawania plików wykonywalnych i pozostać przy czystych funkcjach Windows.
[edytuj] pełna zawartość mojego pliku PS1 minus rzeczywista nazwa bazy danych. Celem skryptu jest wykonanie kopii zapasowej zestawu bazy danych SQL, a następnie skompresowanie kopii zapasowych w jednym pakiecie w folderze o bieżącej dacie:
$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
źródło
Write-Host "Press any key to continue ..."
Linia 1 : Linia 2:$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Odpowiedzi:
W końcu znalazłem czysty sposób, bawiąc się właściwościami obiektów com. W szczególności następujący fragment kodu może przetestować, czy plik znajduje się w pliku zip:
Pełny skrypt jest następujący:
źródło
Ponieważ działało dobrze po ręcznym wstrzymaniu go, oto tymczasowy hack, którego możesz użyć, dopóki nie zostanie znalezione „właściwe” rozwiązanie. Ogólnie rzecz biorąc, używanie „opóźnień” i „liczników czasu” w ten sposób NIE jest tym, co zrobiłbyś dla rzeczy o kluczowym znaczeniu. To powiedziawszy, dopóki nie znajdziesz lepszej odpowiedzi, możesz to zrobić i sprawdzić, czy to działa:
Wykonaj proces ręcznie kilka razy i CZAS, ile czasu zajmuje zwykle skompresowanie. Jeśli rozmiar bazy danych jest generalnie taki sam każdego dnia, czas potrzebny do ukończenia prawdopodobnie będzie średnio mniej więcej w tym samym czasie.
Załóżmy, że w testach ręcznych masz średnio 60 sekund. Bądź konserwatywny i pomnóż go przez około 4, ponieważ prawdopodobnie nie zajmie to 4 razy dłużej niż zwykle w „normalne” dni. Masz teraz 240 sekund (60 sekund średnio 4).
Na razie więc zamiast kodu „naciśnij dowolny klawisz, aby kontynuować”, zamień go na OPÓŹNIENIE w kodzie, aby skrypt zawiesił się na chwilę, aby poczekać, aż zip się skończy. Wymaga to drobnych poprawek i zgadywania terminów i nie jest dobrym podejściem. Ale w mgnieniu oka ...
W każdym razie, jeśli chcesz spróbować, zmień kod na:
Jeśli używasz PowerShell V1:
Jeśli używasz programu PowerShell V2, użyj zamiast tego polecenia cmdlet Sleep:
Aby zadzierać z czasami w wersji 1, używa milisekund. (Więc 10 sekund = 10000)
Aby zadzierać z czasami w V2, używa sekund. (240 = 240 sekund)
Nigdy nie użyłbym tego w produkcji, ale jeśli nie jest to tak wielka sprawa, a okaże się, że działa dobrze w 99% przypadków, może być wystarczająco dobry.
źródło