4

Мне нужно определить, запущен ли пакетный файл или нет.

В пакете я просто зацикливаюсь и многократно выполняю некоторые задачи. Иногда цикл не может завершиться. Я запускаю командный файл в назначенное время. Когда он запускается, он должен проверить, существует ли предыдущий экземпляр и исправен ли он. Если он не существует, он должен начать новый экземпляр. Если существующий экземпляр не является исправным / устаревшим, новый экземпляр должен уничтожить / отменить более старый процесс.

Несколько теорий:

  1. Как я могу обнаружить предыдущий экземпляр в командном файле? Могу ли я дать идентификатор как вещь?
  2. Я могу использовать импульсный механизм, чтобы сказать, что партия здорова; т.е. глобальная переменная, вновь созданный фиктивный файл, временная метка в фиктивном файле и т. д.
  3. Могу ли я обнаружить своего рода «тайм-аут», если истек последний импульс, то разрешить новый экземпляр ...

Да, я все хочу, чтобы они были в пакетном файле - вместо приложения Windows - я думаю, что это не так сложно для того, кто является наркоманом пакетного файла и принимает этот вопрос как вызов!

5 ответов5

6

Это довольно сложно с точки зрения пакетного файла, в основном потому, что для пакетного файла не существует гарантированного метода обнаружения экземпляров в разных процессах.

ИМО, я думаю, что вы используете не тот инструмент. Хотя, да, это проблема, более важная проблема - «Работает ли это?"

Я запускаю пакетный файл в назначенное время.

Это говорит мне о том, что вы должны использовать программу планирования, например, Task Scheduler. Планировщик заданий гарантирует, что существует только один экземпляр вашего файла, даже если он запущен другим пользователем.

Например, если вы запускаете задачу каждые 30 минут, вы настраиваете задачу на выполнение в течение 30 минут. Затем вы устанавливаете настройку "если задача уже выполняется" на "не создавать новый экземпляр". Или вам нужно выбрать "убить старый", "Запустить другой экземпляр" или «Запустить новое задание, как только будет выполнено старое».

Для Vista и выше: http://technet.microsoft.com/en-us/library/cc748993.aspx#BKMK_cmd
Для XP и выше: http://technet.microsoft.com/en-us/library/bb490996.aspx

Сложным методом является создание состояний.

  1. Ваш пакетный файл входит в исполнение
  2. Ваш пакетный файл зацикливается
  3. Ваш пакетный файл заканчивается

Вы можете создать произвольный файл в% allusersprofile%, например >>Startmybatchfile.txt echo 1 для каждого состояния, а затем проверить, существует ли он. Это также поможет с устранением неисправностей. Но не удаляйте файл в конце. Просто измените его на конечное состояние. Таким образом, будет определенным, если ваш командный файл действительно завершен и больше не работает.

Я бы не использовал глобальную переменную, так как для ее запуска требуются права администратора.

Вы также можете использовать переменную% temp%, но она меняется от пользователя к пользователю. Я полагаю, что это не проблема, если вы являетесь пользователем с одним логином.

5

Это мой последний и самый надежный скрипт:

:init
set "started="
2>nul (
 9>"%~f0.lock" (
  set "started=1"
  call :start
 )
)
@if defined started (
    del "%~f0.lock" >nul 2>nul
) else (
    echo Process aborted: "%~f0" is already running
    @ping localhost > nul
)

exit /b




:start
cd /d %~dp0
:: REST OF THE SCRIPT
2

Лучший способ сделать это - установить заголовок в пакетном скрипте.

Оттуда вы можете использовать список задач для запроса количества запущенных процессов по имени заголовка. Ниже приведен фрагмент кода ниже process_count = 2 означает, что процесс не запущен, 3 означает, что ровно 1 экземпляр запущен, а если выше 3, выполняется более 3 процессов.

Hackish, но партия, что это такое.

Для сценария с непрерывным циклом я недостаточно умен, чтобы придумать способ записать время начала в поле заголовка и запросить его из другого пакетного сценария. Я думал о том, чтобы скопировать и переименовать имя файла со временем, а затем выполнить его, но для меня все, кроме прямого запроса, является более беспорядочным, чем оно того стоит.

Да, я могу перейти в текстовый файл, но я беспокоюсь о повреждении кэша.

@REM ########################
:COUNT_PROCESS_INSTANCES
@REM @@@@@@@@@@@@@@@@@@@@@@@@

SET /A PROCESS_COUNTER=0


@REM No way to inject a counter into this for statement.
FOR /F "DELIMS=" %%A in ('tasklist /FI "WINDOWTITLE EQ %USERNAME%:  %PROCESS_NAME_TO_COUNT%*"') do (CALL :SUB_INCRIMENT_PROCESS_COUNTER)

@REM If one instance is running, or no instances, that's fine.  If more than one instance is running, time to exterminate.

@ECHO PROCESS COUNTER IS: "%PROCESS_COUNTER%"
SET PROCESS_NAME_TO_COUNT=NULL

GOTO :EOF

:SUB_INCRIMENT_PROCESS_COUNTER
SET /A PROCESS_COUNTER=%PROCESS_COUNTER%+1
GOTO :EOF

@REM @@@@@@@@@@@@@@@@@@@@@@@@
@REM ########################

Также для всех, кто заинтересован; Вот как найти PID текущего выполняющегося процесса. Теперь, если вы передадите переменную в подпрограмму, вы можете передать эту переменную обратно по цепочке через текстовый документ (я думаю, что есть и другой способ сделать это, но это ускользает от меня).

@REM @@@@@@@@@@@@@@@@@@@@@@@@
:FIND_SELF_PID
@REM ########################
@REM Leaving this here for later if needed.  This was a byproduct of a bad approach.

FOR /F "TOKENS=1,2,*" %%A in ('tasklist /FI "WINDOWTITLE EQ %USERNAME%:  %CURRENT_TITLE%"') do (SET SELF_PID=%%B)

echo %self_pid%

GOTO :EOF
@REM @@@@@@@@@@@@@@@@@@@@@@@@
@REM ########################
1

Что касается первой части вопроса, если вы используете tasklist.exe для поиска процесса, обратите внимание, что фильтр 'windowtitle' будет работать только для окон в отдельном процессе. Поэтому, если вы хотите, чтобы пакетный файл обнаружил себя (т.е. проверил, есть ли уже запущенный экземпляр), вы должны использовать вместо него 'imagename':

tasklist /V /NH /FI "imagename eq cmd.exe"| find /I /C "UNIQUETITLE" > nul

Также обратите внимание, что без фильтра процесс может занять очень много времени в более поздних версиях Windows. Вот пример пакетного файла, который может быть запущен только как один экземпляр за раз:

@echo off
:: Test with tasklist.exe to make sure the process is not already running:
:: Window title must be unique.
set loop_batch_title=Only this instance is allowed to run.
:: Run tasklist in verbose mode (which returns window titles) and search for the window title of this batch file:
tasklist /V /NH /FI "imagename eq cmd.exe"| find /I /C "%loop_batch_title%" > nul
:: If the window title is found then the errorlevel variable will be 0, which means the process is already running:
if %errorlevel%==0 goto AlreadyRunning
:: If the process isn't already running then the window title can be set:
title %loop_batch_title%

::Put your program loop here
:Loop
echo This is a loop.
ping -n 3 127.0.0.1>nul
goto Loop

:AlreadyRunning
echo.
echo Process already running.
endlocal
0

Я только что закончил пакетный файл, который обеспечивает бесперебойную работу как самого контрольного файла, так и файла заданий -facerecog.exe. Я использовал hello.exe в качестве инструмента тестирования, вы можете заменить свой собственный; то есть. ping.exe и т. д. Когда он запускается без каких-либо параметров, он запускает другой экземпляр и завершает работу, поэтому вы можете запускать его как запланированную задачу через определенные промежутки времени. Я надеюсь, что это поможет кому-то, кто не хочет использовать продвинутые инструменты.

    @goto code

    :comments
    @REM RunMagick for FaceRecog
    @REM Runs FaceRecog forever
    @REM Author Emin Akbulut eminakbulut@gmail.com
    @REM 29/11/2011
    @REM This code is freeware


    :code
    @C:
    @CD C:\NET\FaceRecog

    :settings
    set jobexe=FaceRecog.exe
    set remotehost=192.168.35.211
    set remoteport=1333
    @REM Timeout for response of the batch, in seconds
    set /a timeout=120
    set batchtitle=Monitoring FaceRecog...


    :requirements
    @if NOT exist "stopwatch.exe" (
    @echo. stopwatch.exe is missing!
    @ping localhost > nul
    start http://www.jfitz.com/dos/stopwatch10.zip
    goto end
    ) 




    :menu
    @if "%1" == "/monitor" goto monitor
    @if "%1" == "/stop" goto request-stop
    @if "%1" == "/force-stop" goto stop


    :batch-instance-check 
    tasklist /v /fi "IMAGENAME eq cmd.exe" | find /I /c "%batchtitle%"
    @if "%ERRORLEVEL%" == "0" goto batch-instance-found
    goto start

    :batch-instance-found
    @REM Check the previous instance is healthy/responsive or not
    stopwatch stop < pulse.log > elapsed.log
    set /a elapsed=999
    for /F %%a in ('type elapsed.log') do set /a elapsed=%%a
    if %elapsed% gtr %timeout% (
    taskkill /F /FI "WINDOWTITLE eq %batchtitle%*"
    goto start
    ) else (
    @echo Already running...
    @ping localhost > nul
    goto end
    )

    del elapsed.log



    @goto monitor



    :run-job
    @REM Test FaceRecog process
    tasklist | Find "FaceRecog.exe"
    @If Not ErrorLevel 1 Goto monitor

    taskkill /f /im:conime.exe
    @PING 1.1.1.1 -n 1 -w 1000 >NUL
    start FaceRecog.exe
    @REM Take a breathe
    @PING 1.1.1.1 -n 1 -w 1000 >NUL
    @goto run-job

    :start
    @REM Fire another instance with /monitor command and exit
    start "%batchtitle%" /low /I %0 /monitor
    goto end

    :stop
    @REM Stop the job
    taskkill /F /IM:%jobexe%
    @REM Stop the batch
    taskkill /F /FI "WINDOWSTITLE eq %batchtitle%*"
    del stop.log > nul
    title Done
    goto end

    :request-stop
    @REM Ask programs to quit
    @REM %jobexe% /quit
    @REM Take a breathe
    @ping localhost > nul
    stopwatch start > stop.log
    goto end

    :monitor
    stopwatch start > pulse.log
    @REM Test /stop requested
    @if exist stop.log goto stop
    @REM Test FaceRecog process
    tasklist | Find "%jobexe%"
    @If Not ErrorLevel 0 Goto run-job

    :shorttest
    @Echo Shorttest...
    hello.exe %remotehost% %remoteport% | Find "Version" 
    @If Not ErrorLevel 1 Goto job-ok

    :crashcheck
    @REM No response, crashed?
    tasklist | Find "WerFault.exe"
    @If ErrorLevel 1 Goto longcheck
    taskkill /F /IM:WerFault.exe
    @goto shorttest

    :longcheck
    @Echo Longtest...
    @REM Last check, maybe FaceRecog was busy
    hello.exe %remotehost% %remoteport% | Find "Version" 
    @If Not ErrorLevel 1 Goto job-ok

    :nonresponsive
    @REM Kill non-responsive FaceRecog process and wait for a while to auto-revive
    taskkill /F /IM:%jobexe%



    :job-ok
    @echo OK
    @REM 10 seconds to wait then loop again
    @PING 1.1.1.1 -n 1 -w 10000 >NUL
    @goto monitor


    :end

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .