PDA

Показать полную графическую версию : Копирование файла из одной папки в другую в определенные интервалы времени (bat)


pinguindell
06-08-2019, 15:48
Добрый день уважаемые форумчане

Знаю, что такую задачу можно реализовать, с помощью bat файла, но не знаю как его написать, поэтому обращаюсь к Вам за помощью

Есть папки :

Откуда : T:\Откуда
Куда : C:\Users\UserName\Desktop\Куда


Необходимо чтобы bat файл, в 09 :00, в 12:00 и в 17:00, делал копию файла Презентация.pptx, который находится в папке Откуда и помещал его в папку Куда, при этом архивировал его в zip архив. Название архива должно состоять из названия копируемого файла, в нашем случае, это Презентация и даты и времени копирования.

Например Презентация_дата копирования_время копирования

Буду признателен за Вашу помощь

Iska
06-08-2019, 16:56
pinguindell, в пакетных файлах нет встроенных средств для создания архивов. Посему лучшим вариантом из встроенных средств будет использование PowerShell, наподобие:
Add-Type -AssemblyName 'System.IO.Compression.FileSystem'

$sFileName = 'Презентация.pptx'

$sSourceFolder = 'T:\Откуда'
$sDestFolder = [System.IO.Path]::Combine([System.Environment]::GetFolderPath('Desktop'), 'Куда')


$sSourceFileName = [System.IO.Path]::Combine($sSourceFolder, $sFileName)
$sDestFileName = [System.IO.Path]::Combine($sDestFolder, [System.IO.Path]::GetFileNameWithoutExtension($sFileName) + (Get-Date -f '_yyyyMMdd_HHmmss') + '.zip')

if([System.IO.File]::Exists($sSourceFileName)) {
if([System.IO.Directory]::Exists($sDestFolder)) {
Compress-Archive -Path $sSourceFileName -DestinationPath $sDestFileName -Force

if([System.IO.File]::Exists($sDestFileName)) {
Write-Host "Successfully create zip-archive [$sDestFileName]." -ForegroundColor Green
} else {
Write-Host "Can't create zip-archive [$sDestFileName]." -ForegroundColor Red
}
} else {
Write-Host "Can't find destination folder [$sDestFolder]." -ForegroundColor Red
}
} else {
Write-Host "Can't find source file [$sSourceFileName]." -ForegroundColor Red
}


в 09 :00, в 12:00 и в 17:00, »
Создаёте задание в Планировщике, которое будет исполнять скрипт PowerShell с данным кодом в указанное время.

megaloman
06-08-2019, 17:16
pinguindell, Посредством бесплатного архиватора 7Z (имя файла и путь укажите свои,
при наличии в именах файла/папок кириллицы озаботьтесь сохранить скрипт в 866 кодировке) @Echo Off
Set "FileIn=Z:\Box_In\Вязкость.png"
Set "BoxArc=Z:\Box_Arc"

Call :Arc "%FileIn%" "%BoxArc%"
Exit /B

:Arc
If Not Exist %1 (Echo File %1 not found &Exit /B 2)
If Not Exist "%~2\" (Echo Folder %2 not found &Exit /B 2)

FOR /F "tokens=2 delims==." %%d in ('WMIC OS GET LOCALDATETIME /VALUE') DO (
"C:\Program Files\7-Zip\7z.exe" a "%~2\%~n1_%%d.zip" %1 -mx5
)
Exit /BСоздаёте задание в Планировщике, которое будет исполнять скрипт с данным кодом в указанное время. »

pinguindell
07-08-2019, 08:55
Iska, спасибо большое !

и еще небольшое дополнение, для запуска power shell скрипта через планировщик, без отображения окна PowerShell, написал скрипт vbs, который запускает все это великолепие в фоновом режиме, без показа окна консоли

command = "powershell.exe -nologo -noninteractive -command C:\Users\user_name\Desktop\ps_script.ps1"
set shell = CreateObject("WScript.Shell")
shell.Run command,0, false

плюс в самой задаче в планировщике, во вкладке Общие, установить галку внизу "Скрытая задача"

megaloman , также спасибо большое, буду изучать и Ваш вариант

megaloman
07-08-2019, 11:01
написал скрипт vbs, который запускает все это великолепие в фоновом режиме, без показа окна консоли »Ну, раз такая пьянка, то лучше уж всё сделать в VBS. Нагуглил, доработал. FileIn = "Z:\Box_In\Вязкость.png"
BoxArc = "Z:\Box_Arc"

With CreateObject("Scripting.FileSystemObject")
If Not .FileExists(FileIn) Then
WScript.Echo "!!! File " + FileIn + " not found"
WScript.Quit 1
End If

If Not .FolderExists(BoxArc) Then
WScript.Echo "!!! Folder " + BoxArc + " not found"
WScript.Quit 1
End If

DT = CStr(Year(Date)) + Right("0" + CStr(Month(Date)), 2) + Right("0" + CStr(Day(Date)), 2)
DT = DT + Right("0" + CStr(Hour(Time)), 2) + Right("0" + CStr(Minute(Time)), 2) + Right("0" + CStr(Second(Time)), 2)

Zip = BoxArc + "\" + .GetBaseName(FileIn) + "_" + DT + ".zip"

On Error Resume Next

Err.Number = 0
.CreateTextFile(Zip, True).Write "PK" + Chr(5) + Chr(6) + String(18, vbNullChar)
If Err.Number <> 0 Then
WScript.Echo "!!! " + Zip + " " + Err.Description + "(" + CStr(Err.Number) + ")"
WScript.Quit 1
End If

Set App = CreateObject("Shell.Application")

Err.Number = 0
App.Namespace(Zip).CopyHere FileIn
If Err.Number <> 0 Then
WScript.Echo "!!! " + Zip + " " + Err.Description + "(" + CStr(Err.Number) + ")"
WScript.Quit 1
End If
End With
On Error GoTo 0
WScript.Echo "=== " + Zip + " Done"
Оказывается, нечто подобное было на форуме. (http://forum.oszone.net/thread-266361.html) При большом объеме будет окно ожидания.

Iska
07-08-2019, 14:00
App.Namespace(Zip).CopyHere (FileIn) »
Полагаю, эти скобки — лишние.

megaloman
07-08-2019, 16:43
Iska, Скобки там не существенны, хотя лишнее - всегда во вред. Хуже, что скрипт работает корректно только при запуске посредством Wscript, с Cscript писАть в архив не хочет.

Iska
07-08-2019, 17:46
Iska, Скобки там не существенны, »
megaloman, согласен.

Собственно, всю разницу можно увидеть на таком примере:
Option Explicit

Dim strValue


strValue = "AAAA"
SomeSub strValue
WScript.Echo "strValue = " & strValue

WScript.Echo

strValue = "AAAA"
SomeSub (strValue)
WScript.Echo "strValue = " & strValue

WScript.Quit 0

Sub SomeSub(anyValue)
WScript.Echo "anyValue before = " & anyValue
anyValue = anyValue & "+" & anyValue
WScript.Echo "anyValue after = " & anyValue
End Sub

anyValue before = AAAA
anyValue after = AAAA+AAAA
strValue = AAAA+AAAA

anyValue before = AAAA
anyValue after = AAAA+AAAA
strValue = AAAA
По умолчанию, в VBScript параметры передаются по ссылке (в стек кладётся не значение переменной-параметра, а ссылка на саму переменную-параметр), посему, если изменить значение аргумента внутри процедуры/функции — это отразится на значении самого переданного параметра (поскольку на самом деле работа идёт как раз с ним).

Когда аргумент заключается в скобки — тем самым сначала вычисляется выражение в скобках, а затем уже это вычисленное значение передаётся в процедуру/функцию.

В таких языках, как, например, AutoIt — там практически любой вызов оформляется скобками, а вот в VBScript — скобки используются только тогда, когда вызов осуществляется в правой части выражения. В противном случае скобки интерпретируются не как скобки при вызове процедуры/функции, а как необходимость вычисления выражения.

Когда у нас имеется несколько параметров — ничего страшного, попытка такого вызова:
SomeSub (strValue1, strValue2)
приведёт к тому, что возникнет ошибка компиляции «Недопустимо использование скобок при вызове процедуры Sub». А вот если аргумент будет один, как в случае выше:
SomeSub (strValue)
— компилятор просто решит, что «ничего страшного, (strValue) — это просто выражение, которое надо вычислить и затем передать его значение в процедуру/функцию».

Хуже, что скрипт работает корректно только при запуске посредством Wscript, с Cscript писАть в архив не хочет. »
И хочет, и пишет. Просто не успевает, поскольку метод .CopyHere() работает асинхронно, т.е., объект Folder класса Shell.Application не ждёт окончания работы метода, а сразу возвращает управление скрипту, который, соответственно, продолжает свою работу. Боюсь, что на достаточно крупных файлах «обломится» и wscript.exe (сейчас не проверялось, я уже не помню конкретики).

megaloman
07-08-2019, 18:35
Iska, wscript.exe 1,4G съедает без проблем, сscript.exe и с 3 байтами не справляется. Но если вставить sleep (у меня 30 сек) ............
Set App = CreateObject("Shell.Application")
Call App.Namespace(Zip).CopyHere(FileIn)

WScript.Sleep 30 * 1000

If Err.Number <> 0 Then
............
то мой ноут времён куликовской битвы при запуске скрипта с cscript справляется с 190М

Iska
07-08-2019, 19:02
megaloman, посмотрел. Дело совсем не в том, под каким хостом исполняется скрипт — wscript.exe/cscript.exe. Уберите завершающее:
WScript.Echo "=== " + Zip + " Done"
которое не даёт скрипту вовремя завершиться, и посмотрите на получившийся результат :).

Повторяю, метод .CopyHere() работает асинхронно с поздним связыванием VBScript, посему — увы и ах :(.

megaloman
07-08-2019, 19:08
Iska, посмотрите на получившийся результат » Посмотрел, если не дать поспать в cscript, в архив не пишется, wscript справляется, правда, последнее Echo нужно. Но тогда решение не годится для планировщика

Iska
07-08-2019, 19:17
если не дать поспать, в архив не пишется. »
Да, и в этом главная проблема: паузу-то любой длительности сделать можно, а вот как определить момент, когда данный асинхронный метод закончит исполняться?! Особенно, когда нам нужно добавить в архив не один-единственный файл, а целую кучу файлов и папок с разных исходных мест, к примеру. Есть, конечно, обходные пути, но всё это настолько ненадёжно и нестабильно…

megaloman
07-08-2019, 19:21
Iska, В общем, корявство. Можно, конечно, сделать долгий слип, заведомо достаточный для конкретной задачи.

Iska
07-08-2019, 19:26
megaloman, да, конечно.

megaloman
08-08-2019, 11:46
Iska, pinguindell, Для себя делаю резюме:
FileIn = "Z:\Box_In\С Днем Рождения.ppt"

BoxArc = "Z:\Box_Arc"

With CreateObject("Scripting.FileSystemObject")
If Not .FileExists(FileIn) Then
WScript.Echo "!!! File " + FileIn + " not found"
WScript.Quit 1
End If

If Not .FolderExists(BoxArc) Then
WScript.Echo "!!! Folder " + BoxArc + " not found"
WScript.Quit 1
End If

DT = CStr(Year(Date)) + Right("0" + CStr(Month(Date)), 2) + Right("0" + CStr(Day(Date)), 2)
DT = DT + "-" + Right("0" + CStr(Hour(Time)), 2) + Right("0" + CStr(Minute(Time)), 2) + Right("0" + CStr(Second(Time)), 2)

Zip = BoxArc + "\" + .GetBaseName(FileIn) + "_" + DT + ".zip"

On Error Resume Next

Err.Number = 0
.CreateTextFile(Zip, True).Write "PK" + Chr(5) + Chr(6) + String(18, vbNullChar)

If Err.Number <> 0 Then
WScript.Echo "!!! " + Zip + " " + Err.Description + "(" + CStr(Err.Number) + ")"
WScript.Quit 1
End If

Err.Number = 0
Set App = CreateObject("Shell.Application")
Call App.Namespace(Zip).CopyHere(FileIn)

If Err.Number <> 0 Then
WScript.Echo "!!! " + Zip + " " + Err.Description + "(" + CStr(Err.Number) + ")"
WScript.Quit 1
End If
End With
On Error GoTo 0
MsgBox "После окончания процесса записи в архив нажмите ОК"

..............
..............

Call App.Namespace(Zip).CopyHere(FileIn)

WScript.Sleep 300 * 1000

If Err.Number <> 0 Then
WScript.Echo "!!! " + Zip + " " + Err.Description + "(" + CStr(Err.Number) + ")"
WScript.Quit 1
End If
End With
On Error GoTo 0
' MsgBox "После окончания процесса записи в архив нажмите ОК"
Можно, конечно, попробовать обойтись без Sleep Как вариант, посчитать количество файлов в архиве. Но это скользкий путь, так как при архивации большого файла он помещается в архив не сразу целиком, и преждевременное завершение скрипта приведёт к созданию увечного архива.
FileIn = "Z:\Box_In\С Днем Рождения.ppt"
BoxArc = "Z:\Box_Arc"

Arc = "C:\Program Files\7-Zip\7z.exe"

DT = CStr(Year(Date)) + Right("0" + CStr(Month(Date)), 2) + Right("0" + CStr(Day(Date)), 2)
DT = DT + "-" + Right("0" + CStr(Hour(Time)), 2) + Right("0" + CStr(Minute(Time)), 2) + Right("0" + CStr(Second(Time)), 2)

Zip = BoxArc + "\" + CreateObject("Scripting.FileSystemObject").GetBaseName(FileIn) + "_" + DT + ".zip"

Comm = """" + Arc + """ a """ + Zip + """ """ + FileIn + """ -mx5"

On Error Resume Next
R = CreateObject("WScript.Shell").Run(Comm, 0, False)

If Err.Number <> 0 Then
WScript.Echo "Ошибка при вызове архиватора." + vbCrLf + vbCrLf + Comm + vbCrLf + vbCrLf + "Проверьте наличие файлов по указанным путям" + vbCrLf + Err.Description + "(" + CStr(Err.Number) + ")"
WScript.Quit 1
End If
On Error GoTo 0
Поубирал почти все проверки на существование файлов, так как всё равно при исполнении в назначенных заданиях аварийные сообщения никто не увидит. Кстати, а не сделать ли лог-файл этого процесса?

megaloman
09-08-2019, 15:09
pinguindell, можно вообще обойтись без файла со сценарием в назначенных заданиях.mshta vbscript:execute("fRAR="" """"Z:\Box_Arc\С Днем Рождения.ppt_.zip"""""" : fIn="" """"Z:\Box_In\С Днем Рождения.ppt"""""" : RAR=""""""C:\Program Files\WinRAR\Rar.exe"""" a -ep -dh -agYYYYMMDD-HHMMSS"" : R=CreateObject(""WScript.Shell"").Run(RAR+fRAR+fIn, 0, False) :close")Но тут используется уже winrar c его возможностью добавлять к имени файла дату и время.

Serguei Kouzmine
11-08-2019, 22:03
*** let me Google that for you ***


$source_path = 'c:\Users\Serguei\Music\Folder with a ton of Music'
$destination_path = 'c:\temp'
$zip_filename = 'result'
$zip_path = "${destination_path}\${zip_filename}.zip"

[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | out-null
# create empty zip magic

set-content $zip_path ( [byte[]] @( 80, 75, 5, 6 + (, 0 * 18 ) ) ) -encoding byte

$source_folder = ( new-object -com 'Shell.Application' ).NameSpace( $source_path )

$zip_folder = ( new-object -com 'Shell.Application' ).NameSpace( $zip_path )

# запускаем на выполнение в диалог прогресс режиме, конца выполнения не ждём.
$zip_folder.CopyHere( $source_folder, 16 )

$check_count = (get-childitem -path $source_path ).count
$done = $false
# вот теперь ждем по большому

while ($done -eq $false){
try{
$check_zip = [IO.Compression.ZipFile]::OpenRead($zip_path)
write-debug ('Counting items: {0}' -f ($check_zip.Entries).count )
if( ($check_zip.Entries).count -eq $check_count){
$check_zip.Dispose()
$done = $true
break
}
} catch [Exception] {
}
start-sleep -seconds 1
write-host '.' -nonewline
}




© OSzone.net 2001-2012