Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Скриптовые языки администрирования Windows (http://forum.oszone.net/forumdisplay.php?f=102)
-   -   Копирование файла из одной папки в другую в определенные интервалы времени (bat) (http://forum.oszone.net/showthread.php?t=341696)

pinguindell 06-08-2019 15:48 2882891

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

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

Есть папки :

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


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

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

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

Iska 06-08-2019 16:56 2882901

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
}

Цитата:

Цитата pinguindell
в 09 :00, в 12:00 и в 17:00, »

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

megaloman 06-08-2019 17:16 2882906

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

Цитата:

Цитата Iska
Создаёте задание в Планировщике, которое будет исполнять скрипт с данным кодом в указанное время. »


pinguindell 07-08-2019 08:55 2882972

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 2882993

Цитата:

Цитата pinguindell
написал скрипт 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"

Оказывается, нечто подобное было на форуме. При большом объеме будет окно ожидания.

Iska 07-08-2019 14:00 2883037

Цитата:

Цитата megaloman
Код:

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

Полагаю, эти скобки — лишние.

megaloman 07-08-2019 16:43 2883062

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

Iska 07-08-2019 17:46 2883069

Цитата:

Цитата megaloman
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) — это просто выражение, которое надо вычислить и затем передать его значение в процедуру/функцию».

Цитата:

Цитата megaloman
Хуже, что скрипт работает корректно только при запуске посредством Wscript, с Cscript писАть в архив не хочет. »

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

megaloman 07-08-2019 18:35 2883074

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 2883076

megaloman, посмотрел. Дело совсем не в том, под каким хостом исполняется скрипт — wscript.exe/cscript.exe. Уберите завершающее:
Код:

WScript.Echo "=== " + Zip + "  Done"
которое не даёт скрипту вовремя завершиться, и посмотрите на получившийся результат :).

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

megaloman 07-08-2019 19:08 2883078

Iska,
Цитата:

Цитата Iska
посмотрите на получившийся результат »

Посмотрел, если не дать поспать в cscript, в архив не пишется, wscript справляется, правда, последнее Echo нужно. Но тогда решение не годится для планировщика

Iska 07-08-2019 19:17 2883080

Цитата:

Цитата megaloman
если не дать поспать, в архив не пишется. »

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

megaloman 07-08-2019 19:21 2883081

Iska, В общем, корявство. Можно, конечно, сделать долгий слип, заведомо достаточный для конкретной задачи.

Iska 07-08-2019 19:26 2883082

megaloman, да, конечно.

megaloman 08-08-2019 11:46 2883171

Iska, pinguindell, Для себя делаю резюме:
Вариант без стороннего архиватора
Этот вариант не годится для размещения в планировщике, так как обязательно надо отследить завершение архивации. Строка
MsgBox "После окончания процесса записи в архив нажмите ОК"
обязательна.

Код:

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 "После окончания процесса записи в архив нажмите ОК"


Как альтернатива, в предыдущем коде можно вставить заведомо бОльшее время ожидания чем требуется для завершения создания архива. Этот вариант можно поместить в планировщик. Раз архивирование делается с интервалом в часы, то и время ожидания можно поставить большое, например, 300 сек
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 Как вариант, посчитать количество файлов в архиве. Но это скользкий путь, так как при архивации большого файла он помещается в архив не сразу целиком, и преждевременное завершение скрипта приведёт к созданию увечного архива.

Вариант со сторонним архиватором, например, 7Z, запускаемым из-под vbs. Без проблем отработает в назначенных заданиях
Код:

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 2883305

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 2883624

*** 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
}



Время: 08:50.

Время: 08:50.
© OSzone.net 2001-