Скрипты выполняемые интерпретатором CMD.EXE - стандартной консольной оболочкой для Win2000/WinXP/Vista/Seven/Win8/Win2000 Server/Win2003/Win2008.
Иначе говоря - пакетники. Иногда еще их(не вполне оправданно) называют батниками, но классический батник использует возможности обеспечиваемые оболочкой предыдущих систем COMMAND.COM, возможности которого существенно меньше.
@ECHO OFF SET "BEGIN_MARKER=:ENDFILE1" SET "END_MARKER=:ENDFILE2" FOR /F "usebackq tokens=1 delims=:" %%a IN (`FINDSTR /N /B /C:"%BEGIN_MARKER%" "%~0"`) DO SET "SKIP_LINE=%%a" CALL :WORK "%SKIP_LINE%" "%END_MARKER%" "%~0" GOTO :EOF :WORK FOR /F "usebackq skip=%~1 tokens=1 eol=; delims=" %%a IN (`TYPE "%~3"`) DO IF NOT "%%a"=="%~2" (CALL :PROCEDURE "%%a") ELSE (GOTO :EOF) GOTO :EOF :PROCEDURE (ping -n 1 %~1|FIND /I "TTL=")&&( ECHO Комп "\\%~1" Включен - выключаю... shutdown -f /s /m \\%~1 -t 1 )||ECHO Комп "\\%~1" выключен GOTO :EOF :ENDFILE1 PC1 PC2 PC3 :ENDFILE2
Моя попытка навести порядок и унификацию при подключении сетевых дисков в AD.
Возможна работа как через индивидуальные групповые политики, так и через политику AD по-умолчанию.
Идея состоит в следующем: храним описание вариантов подключений в текстовых файлах, с указанием группы для которой это работает, и анализируем при запуске грeппы конкретного пользователя с использованием dsget и dsquery.
Структура хранимых описаний такова: групповые и общедоменные назначачения хранятся в едином файле расположенном в корне NETLOGON(возможны варианты), а пользовательские назначения располагаются в папках с пользовательским именем входа в систему.
Также в отдельных файлах храним описание запускаемых скриптов для групп и пользователей.
Цель - получить прозрачный и универсальный алгоритм входа в систему единый как для обычного входа, так и для терминального, без черезмерного усложнения групповых политик.
Структура решения:
\dsget.exe - файл из RK для получения списка групп пользователя
\dsquery.dll- файл из RK для получения списка групп пользователя
\dsquery.exe- файл из RK для получения списка групп пользователя
\logon.cmd - собственно тело скрипта
\logon.vbs - костыль для запуска без отображения окна консоли - именно он должен быть назначен в качестве логон скрипта
\rs_list.txt - список групповых и общедоменных назначений подключаемых дисков с указанием группы
\sc_list.txt - список групповых и общедоменных назначений выполняемых скриптов с указанием группы
\_SCRIPT\ - папка с групповыми и общедоменными скриптами
\_SCRIPT\1C.cmd - пример скрипта
\USER1\rs_list.txt- список назначений подключаемых дисков для конкретного пользователя(без указания группы)
\USER1\sc_list.txt- список назначений выполняемых скриптов для конкретного пользователя(без указания группы)
\USER2\rs_list.txtсписок назначений подключаемых дисков для конкретного пользователя(без указания группы)
\USER2\sc_list.txt- список назначений выполняемых скриптов для конкретного пользователя(без указания группы)
Прикрепляю файл с телом скрипта и тестовой структурой.
Приветствуются пожелания по доработке ;)
Добавил ключ рекурсивного анализа вхождения в группы.
И на всякий случай тело скрипта в текстовом виде:
@ECHO OFF :: Буквы дисков зарезервированные под пользовательские сетевые подключения SET "EXLUDE_LETTER=D: E: F: J: H: I: J:" "%~dp0dsquery.exe" user -d "%USERDOMAIN%" -samid "%USERNAME%"|^ %~dp0dsget.exe user -memberof |findstr /I /C:"CN=Domain Users,"&&( :: Удаление текущих сетевых подключений FOR /F "usebackq tokens=2 delims= " %%a IN (`net use^|find ":"^|find "\\"^|FINDSTR /V /I "%EXLUDE_LETTER%"`) DO ( 1>NUL 2>&1 NET USE /DELETE %%a) :: Добавление групповых сетевых подключений IF EXIST "%~dp0rs_list.txt" ( FOR /F "usebackq tokens=1,2,3 eol=; delims=|" %%a IN (`TYPE "%~dp0rs_list.txt"^|FIND "\\"`) DO ( "%~dp0dsquery.exe" user -d "%USERDOMAIN%" -samid "%USERNAME%"|^ %~dp0dsget.exe user -memberof -expand |1>NUL 2>&1 findstr /I /C:"CN=%%~c,"&&( 1>NUL 2>&1 NET USE %%a: %%b /PERSISTENT:NO))) :: Выполнение групповых скриптов IF EXIST "%~dp0sc_list.txt" ( FOR /F "usebackq tokens=1,2 eol=; delims=|" %%a IN (`TYPE "%~dp0sc_list.txt"^|FINDSTR /I ".cmd .bat"`) DO ( "%~dp0dsquery.exe" user -d "%USERDOMAIN%" -samid "%USERNAME%"|^ %~dp0dsget.exe user -memberof -expand |1>NUL 2>&1 findstr /I /C:"CN=%%~b,"&&( IF EXIST "%~dp0%%~a" CALL "%~dp0%%~a"))) :: Добавление пользовательских сетевых подключений IF EXIST "%~dp0%USERNAME%\rs_list.txt" ( FOR /F "usebackq tokens=1,2 eol=; delims=|" %%a IN (`TYPE "%~dp0%USERNAME%\rs_list.txt"^|FIND "\\"`) DO ( 1>NUL 2>&1 NET USE %%a: %%b /PERSISTENT:NO)) :: Выполнение пользовательских скриптов IF EXIST "%~dp0%USERNAME%\sc_list.txt" ( FOR /F "usebackq tokens=1 eol=; delims=|" %%a IN (`TYPE "%~dp0%USERNAME%\sc_list.txt"^|FIND /I ".cmd"`) DO ( IF EXIST "%~dp0%%~a" CALL "%~dp0%%~a")) ) EXIT 0
Известная, но слабоосвещенная тема - обработка в коммандном интерпретаторе CMD данных со спецсимволами.
В большинстве ситуаций она вполне решаема...
Плюс к этому периодически возникают задачи вывода в файл без перевода строки.
Несколько лет назад (на ру-борде) я выкладывал семпловый код, который достаточно подробно иллюстрирует примеры возможной работы:
@ECHO OFF&CLS
(ECHO Обработка переменных со спецсимволами "(", ")","&","|",">","<")
(ECHO SET "AAA=сообщение()&|<>команда")
SET "AAA=сообщение()&|<>команда"(ECHO Вывод на экран с обрамляющими двойными кавычками)
(ECHO ^(ECHO "%%AAA%%"^))
(ECHO "%AAA%")(ECHO Перенаправление значения переменной в файл file.txt с переводом строки то есть с CR/LF)
(ECHO с заменой существующего file.txt)
(ECHO SET /p"=%%AAA%%"^<nul 1^>file.txt^&ECHO.^>^>file.txt)
SET /p"=%AAA%"<nul 1>file.txt&ECHO.>>file.txt(ECHO Перенаправление значения переменной в файл file.txt с переводом строки то есть с CR/LF)
(ECHO дописыванием в существующий file.txt)
(ECHO SET /p"=%%AAA%%"^<nul 1^>^>file.txt^&ECHO. 1^>^>file.txt)
SET /p"=%AAA%"<nul 1>>file.txt&ECHO. 1>>file.txt(ECHO Перенаправление значения переменной в файл file1.txt без перевода строки то есть без CR/LF)
(ECHO с заменой существующего file1.txt)
(ECHO SET /p"=%%AAA%%"^<nul 1^>file1.txt)
SET /p"=%AAA%"<nul 1>file1.txt(ECHO Перенаправление значения переменной в файл file1.txt без перевода строки то есть без CR/LF)
(ECHO с дописыванием в существующий file1.txt)
(ECHO SET /p"=%%AAA%%"^<nul 1^>^>file1.txt)
SET /p"=%AAA%"<nul 1>>file1.txt(ECHO Вывод значения переменной на экран с переводом строки то есть с CR/LF)
(ECHO SET /p"=%%AAA%%"^<nul^&ECHO.)
(ECHO ^(ECHO Вторая строка^))
SET /p"=%AAA%"<nul&ECHO.
(ECHO Вторая строка)(ECHO Вывод значения переменной на экран без перевода строки то есть с CR/LF)
(ECHO SET /p"=%%AAA%%"^<nul)
(ECHO ^(ECHO Вторая строка^))
SET /p"=%AAA%"<nul
(ECHO Вторая строка)(ECHO Метод предварительной подготовки: )
(ECHO SET "AAA=%%AAA:&=^&%%")
(ECHO SET "AAA=%%AAA:|=^|%%")
(ECHO SET "AAA=%%AAA:<=^<%%")
(ECHO SET "AAA=%%AAA:>=^>%%")
(ECHO SET "AAA=%%AAA:(=^(%%")
(ECHO SET "AAA=%%AAA:)=^)%%")
SET "AAA=%AAA:&=^&%"
SET "AAA=%AAA:|=^|%"
SET "AAA=%AAA:<=^<%"
SET "AAA=%AAA:>=^>%"
SET "AAA=%AAA:(=^(%"
SET "AAA=%AAA:)=^)%"
(ECHO ^(ECHO %%AAA%%^))
(ECHO %AAA%)
PAUSE
Кодировка приведенного скрипта CP866.
Для ознакомления рекомендуется выполнить ;)
Само-собой в случае вопросов постараюсь ответить...
Убить все отключенные сессии:
@ECHO OFF FOR /F "USEBACKQ TOKENS=2 DELIMS= " %%a IN (`quser^|findstr /b /v "^>"^|findstr /i /v " ID "^|findstr /v /i "rdp-tcp"`) DO logoff %%~a EXIT 0
Преобразование столбца в строку с заданными разделителями и обрамлением. Кодировка скрипта CP866.
Символ двойной кавычки при этом не удастся использовать как разделитель или обрамление.
Принцип работы такой: на входе файл с набором строк, на выходе файл с одной строкой где исходные строки обрамлены нужным разделителем(на самом деле набором символов) и разделяются заданным разделителем(также набор символов).
@ECHO OFF :: Принимает четыре параметра из командной строки: :: 1 - имя файла для обработки (обязательный!) :: 2 - имя файла результата (необязательный!) :: при незаданном параметре будет использован файл result.txt в текущей папке SET FILE_RESULT=%~dp0result.txt :: 3 - последовательность символов как разделитель для вывода (необязательный!) :: при незаданном параметре будет использован символ :: При указании ОБЯЗАТЕЛЬНО параметр брать в двойные кавычки!!! SET RAZDEL=, :: 4 - символ который будет использоваться для обрамления вывода по текущей строке (необязательный!) :: при незаданном параметре будет использован символ ' :: При указании ОБЯЗАТЕЛЬНО параметр брать в двойные кавычки!!! SET FRAME=' IF %~1==( ECHO Имя файла для обработки не указано! GOTO :ERR ) IF NOT EXIST %~1 ( ECHO Файл для обработки не существует! GOTO :ERR ) IF NOT %~2== ( SET FILE_RESULT=%~2 ) IF NOT %~3== ( SET RAZDEL=%~3 ) IF NOT %~4== ( SET FRAME=%~4 ) FOR /F usebackq tokens=* delims= %%a IN (%~1) DO CALL :MAIN %%~a GOTO :EOF :MAIN SET /p=%RAZDEL1%%FRAME%%~1%FRAME%<nul>>%FILE_RESULT% SET RAZDEL1=%RAZDEL% GOTO :EOF :ERR PAUSE GOTO :EOF
Идем в ветку реестра
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate]
Экспортируем ее в файл и добавляем в реестр ПК в домен не входящих. Это приведет к обращению на сервер указанный в ветке. Для проверки запускаем в командной строке: wuauclt /detectnow Смотрим логи C:\windows\WindowsUpdate.log, идем в конец файла и находим строку вида:
2013-03-23 18:13:56:375 908 1074 PT +++++++++++ PT: Synchronizing server updates +++++++++++ 2013-03-23 18:13:56:375 908 1074 PT + ServiceId = {3DA21691-E39D-4DA6-8A4B-B43877BCB1B7}, Server URL = http://192.168.0.10:8080/ClientWebService/client.asmx
где 192.168.0.10 - имя сервера, на котором у нас крутится WSUS. Если нет, проверяем значения ключей реестра
HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\WUServer HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\WUStatusServer
их значение должно соответствовать 192.168.0.10:8080. Если соответствует, перезагружаемся и проверяем еще раз, если не соответствует - правим значения, перезагружаемся, проверяем. Ветка реестра, отвечающий за настройку политики WindowsUpdate в общем виде выглядит приблизительно так:
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate] "AcceptTrustedPublisherCerts"=dword:00000001 "WUServer"="http://192.168.0.10:8080" "WUStatusServer"="http://192.168.0.10:8080" [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU] "NoAutoUpdate"=dword:00000000 "AUOptions"=dword:00000003 "ScheduledInstallDay"=dword:00000000 "ScheduledInstallTime"=dword:00000010 "NoAutoRebootWithLoggedOnUsers"=dword:00000001 "DetectionFrequencyEnabled"=dword:00000001 "DetectionFrequency"=dword:00000008 "UseWUServer"=dword:00000001
Лично мне периодически приходилось сталкиваться с обработкой данных не в табличной форме, а в "именованном формате" то есть когда каждый параметр пишется на отдельной строке в виде Параметр=Значение параметра причем файл имеет строго выраженную периодическую структуру и любой из параметров является необязательным. Порядок следования - произвольный.
Пример такого формата - стандартный формат 1С для общения с клиент-банками 1CClientBankExchange
На самом деле его можно даже средствами "малой механизации"(то есть чистый CMD-скрипт) преобразовать в табличную форму для дальнейшей обработки.
Привожу пример скрипта(кодировка Win1251):
@ECHO OFF CLS COLOR 1E CHCP 1251>NUL 2>&1 :: Папка с файлами для обработки. Полный путь в WIN1251!!! SET "INPUT_DIR=D:\DATABASE" :: Папка с выходными файлами. Полный путь в WIN1251!!! :: Может совпадать с исходной папкой. SET "OUTPUT_DIR=D:\DATABASE" :: Папка для бэкапа входящих файлов. Полный путь в WIN1251!!! :: Если она не указана то исходные файлы удалятся!!! SET "BACKUP_DIR=D:\DATABASE\BACKUP" :: Маска файлов для обработки SET "FILE_MASK=to1c????.txt" IF NOT EXIST "%INPUT_DIR%\" (ECHO Папка с файлами для обработки не найдена!&GOTO ERROR) 2>NUL DIR /B "%INPUT_DIR%\%FILE_MASK%"|1>NUL FIND /C /V ""||(ECHO Файлы для обработки не найдены!&GOTO ERROR) IF NOT EXIST "%OUTPUT_DIR%" MD "%OUTPUT_DIR%" IF NOT EXIST "%OUTPUT_DIR%" ECHO Не удалось создать папку для выходных файлов!&GOTO ERROR IF NOT "%BACKUP_DIR%"=="" ( IF NOT EXIST "%BACKUP_DIR%" MD "%BACKUP_DIR%" IF NOT EXIST "%BACKUP_DIR%" ECHO Не удалось создать папку для бэкапа файлов!&GOTO ERROR ) ECHO Представляем системную дату в удобном виде: ECHO wscript.ECHO YEAR(DATE) ^& RIGHT(0 ^& MONTH(DATE),2) ^& RIGHT(0 ^& DAY(DATE),2)>"%TEMP%\tmp.vbs" FOR /F %%i IN ('cscript "%TEMP%\tmp.vbs" //Nologo') DO SET "TEKDATA=%%i" IF EXIST "%TEMP%\tmp.vbs" DEL "%TEMP%\tmp.vbs" ECHO %TEKDATA% SETLOCAL ENABLEDELAYEDEXPANSION FOR /F "usebackq" %%i IN (`DIR /B /A:-D "%INPUT_DIR%\%FILE_MASK%"`) DO (0<"%INPUT_DIR%\%%i" SET /P "LINE_1=") &IF /I "!LINE_1!"=="1CClientBankExchange" CALL :MAIN "%%i" ENDLOCAL EXIT :MAIN SET "FILE_NAME=%~1" SET "NEWFILE_NAME=%TEKDATA%_%~1" ECHO Выводим строку заголовков 1>"%OUTPUT_DIR%\%NEWFILE_NAME%" (ECHO 01-Номер^|02-Дата^|03-Сумма^|04-ДатаСписано^|05-Плательщик^|06-ПлательщикИНН^|07-ПлательщикКПП^|08-Плательщик1^|09-ПлательщикСчет^|10-ПлательщикРасчСчет^|11-ПлательщикБанк1^|12-ПлательщикБанк2^|13-ПлательщикБИК^|14-ПлательщикКорсчет^|15-ДатаПоступило^|16-Получатель^|17-ПолучательИНН^|18-ПолучательКПП^|19-Получатель1^|20-ПолучательСчет^|21-ПолучательРасчСчет^|22-ПолучательБанк1^|23-ПолучательБанк2^|24-ПолучательБИК^|25-ПолучательКорсчет^|26-ВидПлатежа^|27-ВидОплаты^|28-СтатусСоставителя^|29-ПоказательКБК^|30-ОКАТО^|31-ПоказательОснования^|32-ПоказательПериода^|33-ПоказательНомера^|34-ПоказательДаты^|35-ПоказательТипа^|36-СрокПлатежа^|37-Очередность^|38-НазначениеПлатежа^|39-Имя файла для приема^|) SET "POINTER=" SETLOCAL ENABLEDELAYEDEXPANSION FOR /F "USEBACKQ TOKENS=1* DELIMS== " %%a in ("%INPUT_DIR%\%FILE_NAME%") DO ( IF "%%a"=="КонецДокумента" CALL :TOFILE&SET "POINTER=0" IF !POINTER!==1 (IF NOT "%%~b"=="" ( IF /I "%%~a"=="Номер" SET "PP_1=%%~b" IF /I "%%~a"=="Дата" SET "PP_2=%%~b" IF /I "%%~a"=="Сумма" SET "PP_3=%%~b" IF /I "%%~a"=="ДатаСписано" SET "PP_4=%%~b" IF /I "%%~a"=="Плательщик" SET "PP_5=%%~b" IF /I "%%~a"=="ПлательщикИНН" SET "PP_6=%%~b" IF /I "%%~a"=="ПлательщикКПП" SET "PP_7=%%~b" IF /I "%%~a"=="Плательщик1" SET "PP_8=%%~b" IF /I "%%~a"=="ПлательщикСчет" SET "PP_9=%%~b" IF /I "%%~a"=="ПлательщикРасчСчет" SET "PP_10=%%~b" IF /I "%%~a"=="ПлательщикБанк1" SET "PP_11=%%~b" IF /I "%%~a"=="ПлательщикБанк2" SET "PP_12=%%~b" IF /I "%%~a"=="ПлательщикБИК" SET "PP_13=%%~b" IF /I "%%~a"=="ПлательщикКорсчет" SET "PP_14=%%~b" IF /I "%%~a"=="ДатаПоступило" SET "PP_15=%%~b" IF /I "%%~a"=="Получатель" SET "PP_16=%%~b" IF /I "%%~a"=="ПолучательИНН" SET "PP_17=%%~b" IF /I "%%~a"=="ПолучательКПП" SET "PP_18=%%~b" IF /I "%%~a"=="Получатель1" SET "PP_19=%%~b" IF /I "%%~a"=="ПолучательСчет" SET "PP_20=%%~b" IF /I "%%~a"=="ПолучательРасчСчет" SET "PP_21=%%~b" IF /I "%%~a"=="ПолучательБанк1" SET "PP_22=%%~b" IF /I "%%~a"=="ПолучательБанк2" SET "PP_23=%%~b" IF /I "%%~a"=="ПолучательБИК" SET "PP_24=%%~b" IF /I "%%~a"=="ПолучательКорсчет" SET "PP_25=%%~b" IF /I "%%~a"=="ВидПлатежа" SET "PP_26=%%~b" IF /I "%%~a"=="ВидОплаты" SET "PP_27=%%~b" IF /I "%%~a"=="СтатусСоставителя" SET "PP_28=%%~b" IF /I "%%~a"=="ПоказательКБК" SET "PP_29=%%~b" IF /I "%%~a"=="ОКАТО" SET "PP_30=%%~b" IF /I "%%~a"=="ПоказательОснования" SET "PP_31=%%~b" IF /I "%%~a"=="ПоказательПериода" SET "PP_32=%%~b" IF /I "%%~a"=="ПоказательНомера" SET "PP_33=%%~b" IF /I "%%~a"=="ПоказательДаты" SET "PP_34=%%~b" IF /I "%%~a"=="ПоказательТипа" SET "PP_35=%%~b" IF /I "%%~a"=="СрокПлатежа" SET "PP_36=%%~b" IF /I "%%~a"=="Очередность" SET "PP_37=%%~b" IF /I "%%~a"=="НазначениеПлатежа" SET "PP_38=%%~b" SET "PP_39=\\%COMPUTERNAME%@%INPUT_DIR%\%FILE_NAME%" ) ) IF /I "%%a"=="СекцияДокумент" (SET "POINTER=1"&CALL :INIT) ) ENDLOCAL ECHO Формирование файла "%OUTPUT_DIR%\%NEWFILE_NAME%" формата текст с разделителями завершено! IF NOT EXIST "%BACKUP_DIR%\%TEKDATA%" MD "%BACKUP_DIR%\%TEKDATA%" IF NOT "%BACKUP_DIR%"=="" (MOVE /Y "%INPUT_DIR%\%FILE_NAME%" "%BACKUP_DIR%\%TEKDATA%") ELSE (DEL /Q /F "%INPUT_DIR%\%FILE_NAME%") GOTO :EOF :INIT FOR /L %%z IN (1,1,39) DO SET "PP_%%z=" GOTO :EOF :TOFILE ECHO Вывод платежки N %PP_1% в файл "%OUTPUT_DIR%\%NEWFILE_NAME%" FOR /L %%z IN (1,1,39) DO ( 1>>"%OUTPUT_DIR%\%NEWFILE_NAME%" SET /P "=!PP_%%z!^|">"%OUTPUT_DIR%\%NEWFILE_NAME%" (ECHO.) GOTO :EOF :ERROR PAUSE COLOR
При этом получим на выходе текстовый файл с разделителем "|" между полями.
Первой строкой будет выведена строка заголовков:
01-Номер|
02-Дата|
03-Сумма|
04-ДатаСписано|
05-Плательщик|
06-ПлательщикИНН|
07-ПлательщикКПП|
08-Плательщик1|
09-ПлательщикСчет|
10-ПлательщикРасчСчет|
11-ПлательщикБанк1|
12-ПлательщикБанк2|
13-ПлательщикБИК|
14-ПлательщикКорсчет|
15-ДатаПоступило|
16-Получатель|
17-ПолучательИНН|
18-ПолучательКПП|
19-Получатель1|
20-ПолучательСчет|
21-ПолучательРасчСчет|
22-ПолучательБанк1|
23-ПолучательБанк2|
24-ПолучательБИК|
25-ПолучательКорсчет|
26-ВидПлатежа|
27-ВидОплаты|
28-СтатусСоставителя|
29-ПоказательКБК|
30-ОКАТО|
31-ПоказательОснования|
32-ПоказательПериода|
33-ПоказательНомера|
34-ПоказательДаты|
35-ПоказательТипа|
36-СрокПлатежа|
37-Очередность|
38-НазначениеПлатежа|
39-Имя файла для приема|
Само-собой не в приведенном виде - а в виде одной строки ;)
Относительно простой вариант резервного копирования по списку файлов/папок с учетом типа резервной копии и количества хранимых копий по типам. Классические типы: дневная-недельная-месячная-годовая копии. Кодировка скрипта CP866. В приаттаченом файле содержится сам скрипт, пример файла списка и консольные версии архиватора 7z.
@ECHO OFF CLS SETLOCAL :: Папка для хранения резервных копий - если надо не текущую впишите свою :: По-умолчанию текущая папка SET "BACKUP_DIR=%~dp0" :: Постоянная часть имени архива - для уникальной идентефикации архива SET "SOURCE_NAME=MY_BACKUP_1" :: Имя и месторасположение файла со списком для резервного копирования :: Имена папок пишутся либо с символом "\" в конце либо без него SET "BACKUP_LIST=%~dp0listbackup.txt" :: Кодировка списка - доступные варианты: UTF-8, WIN, DOS SET "LIST_CHARSET=DOS" ::Тип бэкапа: DAY, WEEK, MONTH, YEAR ::День недели число ::1 - Воскресенье ::2 - Понедельник ::3 - Вторник ::4 - Среда ::5 - Четверг ::6 - Пятница ::7 - Суббота :: День недели бэкап которого считается недельным SET "WEEK_DAY_BACKUP=1" :: Бэкап последнего числа месяца считается месячным :: Бэкап последнего числа года считается годовым :: Количество хранимых страховых копий по типам SET "REZERV_DAY=6" SET "REZERV_WEEK=5" SET "REZERV_MONTH=11" SET "REZERV_YEAR=50" :: ПРИМЕЧАНИЕ: имя архива будет иметь маску: :: %SOURCE_NAME%_ГГГГ_ММ_ДД_%TYPE_BACKUP%.7z ECHO. ECHO %DATE% - %TIME:~0,5% Резервное копирование: ECHO тело скрипта: "%~0" ECHO список: "%BACKUP_LIST%" CALL :BACKUP_PATHS CALL :SYS_TYPE CALL :DATE_SET CALL :ARC CALL :REZERV_TRIM ENDLOCAL GOTO :EOF :SYS_TYPE :: Определение разрядности системы для использования нужной версии архиватора ECHO "%PROCESSOR_ARCHITECTURE%""%PROCESSOR_ARCHITEW6432%"|1>NUL 2>NUL FIND /I "AMD64"&&SET "ARC_DIR=%~dp0X64"||SET "ARC_DIR=%~dp0X32" :: Отбрасывание последнего символа "\" в пути архиватора SET "LAST_CHAR=%ARC_DIR:~-1%" IF "%LAST_CHAR%"=="\" SET "ARC_DIR=%ARC_DIR:~0,-1%" IF NOT EXIST "%ARC_DIR%\7z.exe" ( ECHO Не обнаружен архиватор по пути "%ARC_DIR%"! ECHO Положите соответсвующую версию 7z.exe по пути .\X64 или .\X86 и перезапустите скрипт! CALL :ERROR 1 ) GOTO :EOF :BACKUP_PATHS :: Отбрасывание последнего символа "\" в пути бэкапа SET "LAST_CHAR=%BACKUP_DIR:~-1%" IF "%LAST_CHAR%"=="\" SET "BACKUP_DIR=%BACKUP_DIR:~0,-1%" :: Создание целевой папки для резервного копирования с проверкой существования IF NOT EXIST "%BACKUP_DIR%" MD "%BACKUP_DIR%" IF NOT EXIST "%BACKUP_DIR%" ( ECHO Указанная папка для создания копии недоступна! CALL :ERROR 2 ) ECHO Резервная копия будет создана в папке %BACKUP_DIR% GOTO :EOF :DATE_SET :: Определение параметров текущей даты и типа бэкапа ECHO wscript.ECHO YEAR(DATE)^&"_"^&RIGHT(0^&MONTH(DATE),2)^&"_"^&RIGHT(0^&DAY(DATE),2)^&"_"^& DAY(DATESERIAL(YEAR(DATE),MONTH(DATE)+1,1-1))^&"_"^&WEEKDAY(DATE)^&"_"^&RIGHT(0^&hour(TIME),2)^&"_"^&RIGHT(0^&minute(TIME),2) 1>"%TEMP%\tmp.vbs" FOR /F "TOKENS=1,2,3,4,5,6,7 DELIMS=_" %%a IN ('cscript "%TEMP%\tmp.vbs" //Nologo') DO SET "TEK_YEAR=%%a"&SET "TEK_MONTH=%%b"&SET "TEK_DAY=%%c"&SET "LAST_DAY=%%d"&SET "TEK_WEEK=%%e"&SET "TEK_HOUR=%%f"&SET "TEK_MINUTE=%%g" IF EXIST "%TEMP%\tmp.vbs" DEL "%TEMP%\tmp.vbs" SET "TYPE_BACKUP=DAY" IF "%WEEK_DAY_BACKUP%"=="%TEK_WEEK%" SET "TYPE_BACKUP=WEEK" IF "%TEK_DAY%"=="%LAST_DAY%" SET "TYPE_BACKUP=MONTH" IF "%TEK_MONTH%"=="12" (IF "%TEK_DAY%"=="%LAST_DAY%" (SET "TYPE_BACKUP=YEAR")) CALL SET "REZERV_NUM=%%REZERV_%TYPE_BACKUP%%%" SET "ARC_NAME=%SOURCE_NAME%_%TEK_YEAR%_%TEK_MONTH%_%TEK_DAY%~%TYPE_BACKUP%" IF EXIST "%BACKUP_DIR%\%ARC_NAME%.7z" ( ECHO Резервная копия за этот день уже создана! ECHO Удалите или переименуйте ее и запустите скрипт повторно! CALL :ERROR 3 ) GOTO :EOF :ARC IF EXIST "%ARC_DIR%\7z.exe" ( 1>nul "%ARC_DIR%\7z.exe" a -t7z "%BACKUP_DIR%\%ARC_NAME%.7z" @"%BACKUP_LIST%" -ms -mmt -mx7 -bd -scs%LIST_CHARSET% -ssw ECHO Страховая копия создана... ) GOTO :EOF :REZERV_TRIM ECHO Удалены устаревшие резервные копии: FOR /F "SKIP=%REZERV_NUM% USEBACKQ TOKENS=1 DELIMS=" %%a IN (`DIR /O:-N /B "%BACKUP_DIR%\%SOURCE_NAME%_20??_??_??~%TYPE_BACKUP%.7z"`) DO ( DEL /Q /F "%BACKUP_DIR%\%%a" ECHO "%BACKUP_DIR%\%%a" ) GOTO :EOF :ERROR ECHO Произошло досадное недоразумение - резервная копия не создана ... ENDLOCAL EXIT %~1
В случае необходимости ведения лога перенаправьте информационные сообщения скрипта в файл.
Продвинутый кросплатформенный модуль для встраивания в скрипты, позволяющий в удобной форме получать данные из реестра для дальнейшего использования(как всегда - кодировка скрипта CP866):
@ECHO OFF CLS :: 1-й параметр = раздел реестра для чтения :: 2-й параметр = имя считываемого параметра - указать пустые кавычки для чтения параметра "По умолчанию" :: 3-й параметр = имя переменной окружения куда считываем результат :: Пример чтения именованного параметра: CALL :REG_READ "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" "USERINIT" "MY_VAR1" ECHO Именованный параметр значение "%MY_VAR1%" ECHO Именованный параметр тип "%MY_VAR1_TYPE%" :: Пример чтения безымянного параметра "По умолчанию" CALL :REG_READ "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" "" "MY_VAR2" ECHO Параметр "По умолчанию" значение "%MY_VAR2%" ECHO Параметр "По умолчанию" тип "%MY_VAR2_TYPE%" GOTO :EOF :REG_READ IF NOT "%~2"=="" ( FOR /F "USEBACKQ TOKENS=* DELIMS=" %%a IN (`REG QUERY "%~1" /v "%~2" 2^>NUL ^|FINDSTR /I /B /C:" %~2" 2^>NUL`) DO CALL :TRANSLATE "%%a" "%~2" "%~3" 1>NUL 2>&1 REG QUERY "%~1" /v "%~2"||(SET "%~3_TYPE="&SET "%~3=!") ) IF "%~2"=="" ( FOR /F "USEBACKQ TOKENS=* DELIMS=" %%a IN (`REG QUERY "%~1" /ve 2^>NUL ^|FINDSTR /B /C:" " 2^>NUL`) DO CALL :TRANSLATE "%%a" "" "%~3" 1>NUL 2>&1 REG QUERY "%~1" /ve||(SET "%~3_TYPE="&SET "%~3=!") ) CALL SET "%~3=%%%~3:~0,-1%%" GOTO :EOF :TRANSLATE SET "TEMP_STR=%~1" IF NOT "%~2"=="" CALL SET "TEMP_STR=%%TEMP_STR:*%~2=%%" CALL SET "TEMP_STR=%%TEMP_STR:*REG_=%%" FOR /F "USEBACKQ TOKENS=1*" %%a IN ('"%TEMP_STR%"') DO ( SET "%~3_TYPE=REG_%%~a" SET "%~3=%%~b" ) GOTO :EOF
ЗЫ - Что делать с "(значение параметра не задано)" на системах Win7(судя по всему Vista и старше) для пустого параметра "По умолчанию" я пока в универсальном виде не придумал. Такое значение автоматически присваивается неописанному пустому параметру и, к сожалению, оно зависимо от языка системы.