Как игнорировать ошибку, если пакетный скрипт не может записать в выходной файл
У нас есть процесс преобразования, который вызывает много файлов пакетных скриптов Windows и записывает вывод и ошибки в один выходной файл. Иногда выходной файл блокируется, что приводит к завершению сценария. Следовательно, пакетный файл завершается и выдает код ошибки. Есть ли способ игнорировать ошибку, если выходной файл заблокирован? Например, если файл ScriptOutput в приведенном ниже примере заблокирован, возможно ли игнорировать ошибку при перенаправлении вывода и продолжить выполнение сценария?
@echo( >> D:\Conversion\log\ScriptOutput.log 2>>&1
D:\Conversion\log\ScriptOutput.log 2>>&1
@echo %date% %time% BEGIN OUTPUT FOR MoveTransDataToReporting.dtsx Script >> D:\Conversion\log\ScriptOutput.log 2>>&1
@echo( >> D:\Conversion\log\ScriptOutput.log 2>>&1
"C:\Program Files (x86)\Microsoft SQL Server\120\DTS\Binn\DTExec.exe" /FILE "\"D:\Conversion\sql\MoveTransDataToReporting3Years.dtsx\"" /CHECKPOINTING OFF /REPORTING EWCDI >> D:\Conversion\log\ScriptOutput.log 2>&1
@echo( >> D:\Conversion\log\ScriptOutput.log 2>>&1
@echo %date% %time% END OUTPUT FOR MoveTransDataToReporting.dtsx Script >> D:\Conversion\log\ScriptOutput.log 2>>&1
@echo ************************************************************************************************************************************** >> D:\Conversion\log\ScriptOutput.log 2>>&1
2 ответа
set "LogFile=ScriptOutput.log"
:retry
((
REM Do stuff here...
verify>nul &:: Sets errolevel to 0. This is important
)>>"%LogFile%" 2>&1)2>nul || (set "LogFile=con" & echo Log file inaccessible, Bypassing LogFile... & goto :retry)
Последняя команда в блоке LOG должна явно установить уровень ошибки равным 0, поэтому условие ошибки будет выполнено только в том случае, если файл журнала не может быть открыт. verify>nul
сделаю работу. любая другая команда, которая устанавливает errorlevel в 0, может использоваться вместо этого. например: (call )
(обратите внимание на расстояние между call
и закрывающая скобка.)
Внешний блок будет скрывать сообщение об ошибке, связанное с открытием LogFile, поэтому при желании в блоке условия ошибки может отображаться пользовательское сообщение.
Он будет повторять процесс, отображая вывод на консоль, устанавливая LogFile в con
, Он может быть перенаправлен на nul
или другой файл, если это необходимо.
Я предлагаю следующий пакетный файл для решения этой проблемы:
@echo off
for /F "tokens=2,3 delims==.+-" %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "LogFileName=%TEMP%\%~n0_%%I%%J.log"
>>"%LogFileName%" echo/
>>"%LogFileName%" echo %date% %time% BEGIN OUTPUT FOR MoveTransDataToReporting.dtsx Script
>>"%LogFileName%" echo/
"C:\Program Files (x86)\Microsoft SQL Server\120\DTS\Binn\DTExec.exe" /FILE "D:\Conversion\sql\MoveTransDataToReporting3Years.dtsx" /CHECKPOINTING OFF /REPORTING EWCDI >>"%LogFileName%" 2>&1
>>"%LogFileName%" echo/
>>"%LogFileName%" echo %date% %time% END OUTPUT FOR MoveTransDataToReporting.dtsx Script
>>"%LogFileName%" echo **************************************************************************************************************************************
set "RetryCount=0"
:MergeLogs
for %%I in ("%TEMP%\%~n0_????????????????????.log") do (
( type "%%I" >>"D:\Conversion\log\ScriptOutput.log" ) 2>nul && (
del "%%I" & if "%%I" == "%LogFileName%" goto :EOF
) || goto NextRetry
)
goto :EOF
:NextRetry
set /A RetryCount+=1
if %RetryCount% == 30 goto :EOF
echo Retry %RetryCount% to merge the logs.
if exist %SystemRoot%\System32\timeout.exe (
%SystemRoot%\System32\timeout.exe /T 1 /NOBREAK >nul
) else (
%SystemRoot%\System32\ping.exe -n 2 127.0.0.1 >nul
)
goto MergeLogs
Первый цикл FOR выполняет следующую командную строку в отдельном командном процессе, запущенном FOR в фоновом режиме:
C:\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE
Из строки с данными, выводимыми этой командной строкой, берется текущая дата и время с текущей микросекундой (в реальном времени только текущая миллисекунда) в формате YYYYMMDDhhmmssuuuuuu
, Подробное объяснение см. В ответе на %date%, который дает другой результат в пакетном файле при запуске из запланированных задач в Server 2016. Текущая дата и время в формате YYYYMMDDhhmms
присваиваются переменной цикла I
и текущая микросекунда uuuuuu
зацикливать переменную J
,
Эта строка даты / времени объединяется с путем к папке, представленным %TEMP%
и имя командного файла, представленного %~n0
с дополнительным подчеркиванием к полному имени файла журнала с расширением .log
,
Таким образом, файл журнала создается в папке для временных файлов при каждом запуске командного файла. MoveData.bat
с именем вроде MoveData_20180617105952343000.log
за исключением того, что пакетный файл запускается дважды за одну и ту же миллисекунду, что, мы надеемся, никогда не произойдет.
Командные строки ECHO перенаправляются без запаздывающих пробелов во временный файл журнала, если в командных строках ECHO в пакетном файле нет запаздывающих пробелов / табуляций. Командные строки ECHO в рассматриваемом пакетном коде пишут сообщения с пробелом перед оператором перенаправления >>
как конечный пробел в лог-файл.
В командной строке DTExec.exe
и то и другое \"
удалены, потому что я думаю, что они не нужны здесь. Также в командной строке Windows невозможно определить строку аргумента, заключенную в двойные кавычки, которая содержит "
сам как буквальный символ строки аргумента. "
не может быть экранирован в командной строке Windows, ни с \
ни с ^
который интерпретируется как escape-символ командным процессором Windows.
Создание файла журнала с текущей датой / временем, включая текущую миллисекунду в папке для временных файлов, никогда не должно заканчиваться ошибкой.
Затем временный файл журнала должен быть добавлен в файл журнала, документирующий все преобразования. Это может привести к сбою, например, если файл журнала открывается в приложении для просмотра его содержимого, и приложение открывает файл с блокировкой записи.
По этой причине цикл FOR используется для добавления каждого файла журнала, соответствующего указанному шаблону подстановочного знака в папке для временных файлов, в файл сводного журнала.
Основное преимущество формата даты / времени YYYYMMDDhhmmssuuuuuu
заключается в том, что имена файлов, содержащие дату / время в этом формате, отсортированные в алфавитном порядке, в то же время также сортируются по дате с самым старым первым и самым новым последним. Папка для временных файлов по умолчанию находится в разделе NTFS. Файловая система новой технологии возвращает список имен файлов, соответствующих шаблону подстановочного знака, всегда отсортированному в алфавитном порядке. Поэтому цикл FOR обрабатывает существующие временные файлы журнала этого пакетного файла от самого старого до самого нового.
Цикл FOR получает имя временного файла журнала с полным путем, созданным этим пакетным файлом, и выполняет командную строку type "%%I" >>"D:\Conversion\log\ScriptOutput.log"
добавить содержимое этого файла журнала в сводный файл журнала. Командная строка заключена в командный блок с (
а также )
чтобы убедиться, что это было успешно или не удалось.
Временный файл журнала удаляется при добавлении временного файла журнала в файл сводного журнала. И если успешно добавленный файл журнала является тем же файлом журнала, что и этот пакетный файл, только что созданный ранее, выполнение пакетного файла прекращается. В противном случае добавленный файл журнала был более старым, который нельзя было добавить в файл сводного журнала при предыдущем выполнении пакетного файла, поэтому следующий временный файл журнала обрабатывается FOR.
Но если добавление временного файла журнала в файл сводного журнала завершилось неудачно, поскольку файл сводного журнала в настоящее время по какой-либо причине защищен от записи, цикл FOR завершается с переходом на метку. NextRetry
,
Счетчик повторных попыток увеличивается, и если пакетный файл еще 30 раз не пытался добавить временный файл журнала в файл сводного журнала, он ждет одну секунду, используя TIMEOUT (Windows 7 или Windows Server 2008 или любую более позднюю версию Windows) или PING (более старые версии Windows), а затем делает еще одну попытку добавить самый старый из существующих временных файлов журнала в сводный файл журнала.
Преимущества этого метода для управления файлами журналов:
- Каждое выполнение пакетного файла всегда регистрируется, даже если файл сводного журнала защищен от записи во время выполнения пакетного файла.
- Временный созданный файл журнала, который не может быть добавлен в файл сводного журнала, хотя было предпринято 30 попыток из-за того, что файл сводного журнала открывается в приложении для просмотра его содержимого в течение нескольких минут, добавляется позже пакетным файлом при любом последующем выполнении, для которого Сводный файл журнала больше не защищен от записи.
- Ранее созданные временные файлы журнала добавляются в правильном хронологическом порядке к итоговому файлу журнала благодаря формату даты / времени
YYYYMMDDhhmmssuuuuuu
и NTFS возвращает список имен файлов, соответствующих шаблону, отсортированному в алфавитном порядке, что означает в данном случае от самого старого до самого нового файла. - Пакетный файл добавляет только временные файлы журнала из более старых выполнений и временный файл журнала, созданный текущим выполнением, в файл сводного журнала. Таким образом, если пакетный файл выполняется еще раз, когда он уже запущен, и, следовательно, еще один, более новый временный файл журнала уже создан, который еще не содержит всей информации из-за
DTExec.exe
в данный момент выполняется, этот более новый временный файл журнала игнорируется экземпляром выполнения пакетного файла, который был запущен первым.
Может быть проще понять управление файлами журналов, выполнив следующие действия:
- Поместите ECHO влево, начиная с командной строки
DTExec.exe
и сохраните командный файл. - Откройте окно командной строки и запустите пакетный файл один раз из окна командной строки, чтобы создать
D:\Conversion\log\ScriptOutput.log
, - Выполнить команду
attrib +r D:\Conversion\log\ScriptOutput.log
защищать от записи файл краткого журнала для имитации приложения, открывающего файл с блокировкой записи. - Запустите пакетный файл из окна командной строки еще раз. Видно, что 30 раз не удается добавить временный файл журнала при ожидании более 30 секунд.
- Откройте второе окно командной строки и подготовьте выполнение команды
attrib -r D:\Conversion\log\ScriptOutput.log
просто набрав эту командную строку. - Переключитесь в первое окно командной строки и запустите командный файл в третий раз. Пока повторы выполняются с паузой в 1 секунду между каждой попыткой, переключитесь во второе окно командной строки и выполните уже подготовленную командную строку ATTRIB, чтобы удалить атрибут "только для чтения" из файла сводного журнала.
- Вернитесь к первому окну командной строки, и вы увидите, что цикл повторения завершен.
- Удалить ECHO из командной строки, начиная
DTExec.exe
и сохраните командный файл.
открыто D:\Conversion\log\ScriptOutput.log
в текстовом средстве просмотра / редакторе, и он содержит информацию обо всех трех исполнениях командного файла.
Осталась только одна проблема. Может случиться так, что один старый временный файл журнала добавляется дважды в файл сводного журнала. Это происходит при выполнении этого пакетного файла два или более раза в течение 30 секунд в текущем защищенном от записи файле журнала сводных данных, и блокировка записи удаляется, в то время как все запущенные экземпляры пакетного файла находятся в цикле повторных попыток, и время ожидания 1 секунда заканчивается шанс по крайней мере в двух запущенных экземплярах командного файла почти одновременно.
Я не знаю, как часто выполняется пакетный файл и как долго DTExec.exe
требуется, чтобы закончить задачу. Поэтому я не знаю, может ли когда-либо происходить дублирование информации в файле сводного журнала при реальном использовании командного файла. Количество повторов можно уменьшить, например, с 30
в 5
чтобы избежать этого наиболее вероятного очень редкого случая. Однако при этом случае информация не теряется; только одно выполнение командного файла регистрируется дважды в файле сводного журнала.
Конечно, выполнить команды намного проще и просто потерять всю информацию, если файл сводного журнала в настоящее время защищен от записи.
@echo off
setlocal EnableDelayedExpansion
(
echo/
echo %date% %time% BEGIN OUTPUT FOR MoveTransDataToReporting.dtsx Script
echo/
"C:\Program Files (x86)\Microsoft SQL Server\120\DTS\Binn\DTExec.exe" /FILE "D:\Conversion\sql\MoveTransDataToReporting3Years.dtsx" /CHECKPOINTING OFF /REPORTING EWCDI 2>&1
echo/
echo !date! !time! END OUTPUT FOR MoveTransDataToReporting.dtsx Script
echo **************************************************************************************************************************************
) >>D:\Conversion\log\ScriptOutput.log
endlocal
Задержанное расширение включается по мере необходимости для обращения к динамическим переменным среды DATE и TIME во второй раз с использованием отложенного расширения переменной среды с !date! !time!
,
Командный процессор Windows заменяет каждую ссылку на переменную среды, используя синтаксис %variable%
по текущему значению указанной переменной среды уже при разборе всего блока команд, начиная с (
и заканчивая сопоставлением )
перед выполнением команд внутри блока команд. Таким образом, используя дважды %date% %time%
в этом командном блоке без использования отложенного раскрытия будет одна и та же дата и время, дважды записанные в файле журнала.
Также возможно подавить вывод сообщения об ошибке "Отказано в доступе" для файла журнала, который в настоящее время защищен от записи, перенаправив его на NUL устройства с помощью еще одного блока команд.
@echo off
setlocal EnableDelayedExpansion
((
echo/
echo %date% %time% BEGIN OUTPUT FOR MoveTransDataToReporting.dtsx Script
echo/
"C:\Program Files (x86)\Microsoft SQL Server\120\DTS\Binn\DTExec.exe" /FILE "D:\Conversion\sql\MoveTransDataToReporting3Years.dtsx" /CHECKPOINTING OFF /REPORTING EWCDI 2>&1
echo/
echo !date! !time! END OUTPUT FOR MoveTransDataToReporting.dtsx Script
echo **************************************************************************************************************************************
) >>D:\Conversion\log\ScriptOutput.log ) 2>nul
endlocal
Примечание: командная строка с DTExec.exe
содержит только в конце 2>&1
в двух упрощенных пакетных файлах, опубликованных выше, поскольку все, что написано для обработки STDOUT с помощью командного блока, перенаправляется и добавляется (если возможно) в файл журнала.
Чтобы понять используемые команды и то, как они работают, откройте окно командной строки, выполните там следующие команды и полностью прочитайте все страницы справки, отображаемые для каждой команды.
del /?
echo /?
endlocal /?
for /?
goto /?
if /?
ping /?
set /?
setlocal /?
timeout /?
type /?
wmic /?
wmic os /?
wmic os get /?
wmic os get localdateime /?
Смотрите также