Como posso evitar a execução do mesmo arquivo de lote duas vezes para permitir apenas uma instância?

4

Preciso detectar se um arquivo de lote já está em execução ou não.

No lote, simplesmente faço loop e repetidamente faço algumas tarefas. Às vezes, o loop não pode ser concluído. Eu corro o arquivo em lotes em um horário agendado. Quando executado, ele deve verificar se uma instância anterior existe e está íntegra. Se não existir, deve iniciar uma nova instância. Se a instância que existe não estiver saudável / desatualizada, a nova instância deve matar / cancelar o processo antigo.

Algumas teorias:

  1. Como posso detectar uma instância anterior em um arquivo em lotes? Posso dar um ID como coisa?
  2. Eu posso usar um mecanismo de pulso para dizer que o lote é saudável; ou seja, uma variável global, um arquivo fictício recém-criado, um timestamp em um arquivo fictício, etc.
  3. Posso detectar um tipo de "tempo limite", se o último pulso expirar, depois permitir uma nova instância ...

Sim, eu quero todos eles em um arquivo de lote - em vez de um aplicativo do Windows - eu acho que não é tão difícil para alguém que é um viciado em arquivos em lote e leva essa pergunta como um desafio!

    
por Nime Cloud 28.11.2011 / 15:53

5 respostas

5

Este é o meu script mais recente e mais confiável:

: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
    
por 16.07.2014 / 08:15
6

Isso é bastante difícil do ponto de vista do arquivo de lote, principalmente porque não há um método garantido para um arquivo batch detectar as instâncias nos processos.

IMO, acho que você está usando a ferramenta errada. Embora, sim, seja um desafio, a preocupação mais importante é "Funciona?"

I run batch file in a scheduled time.

Isso me diz que você deve estar usando um programa de agendamento, como o Agendador de Tarefas. O Agendador de Tarefas garantirá que haja apenas uma instância do seu arquivo, mesmo se estiver sendo executado em outro usuário.

Por exemplo, se você executar a tarefa a cada 30 minutos, configure a tarefa para ser executada por 30 minutos. Em seguida, defina a configuração "se a tarefa já estiver em execução" como "não crie uma nova instância". Ou você tem que opção de "matar o antigo", "Executar outra instância" ou "Iniciar uma nova tarefa, logo que o antigo é feito".

Para o Vista e para cima: link
Para Xp e versões posteriores: link

O método desafiador é criar estados.

  1. Seu arquivo de lote entra em execução
  2. Seu arquivo em lote faz um loop
  3. Seu arquivo de lote termina

Você pode criar um arquivo arbitrário em% allusersprofile%, como >>Startmybatchfile.txt echo 1 para cada estado, depois verificar se ele existe. Também ajudará na resolução de problemas. Mas não apague o arquivo no final. Apenas mude para End State. Dessa forma, é determinante se o arquivo de lote tiver realmente concluído e não estiver mais em execução.

Eu não usaria uma variável global, pois requer privilégios de administrador para executar.

Você também pode usar a variável% temp% como, mas isso muda de usuário para usuário. Eu acho que não é uma preocupação se você é uma máquina de usuário de login único.

    
por 28.11.2011 / 17:18
2

A melhor maneira que encontrei para fazer isso é definir o título em um script em lote.

A partir daí, você pode usar a lista de tarefas para consultar a contagem de processos em execução pelo nome do título. O trecho abaixo do código abaixo de process_count = 2 significa que o processo não está em execução, 3 significa que exatamente 1 instância está em execução e, se acima de 3, há mais de 3 processos em execução.

Hackish, mas o lote é o que é.

Para um script contínuo de Looping, não sou inteligente o suficiente para pensar em uma maneira de escrever a hora de início no campo de título e consultá-lo em outro script de lote. Eu pensei em copiar e renomear o nome do arquivo com o tempo, em seguida, executar isso, mas para mim, qualquer coisa, mas uma consulta direta é mais confusão do que vale a pena.

Sim, posso usar o arquivo de texto, mas estou preocupado com a corrupção do cache.

@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 ########################

Além disso, para qualquer pessoa interessada; aqui está como encontrar o PID do processo atualmente em execução. Agora, se você passar uma variável para um sub-script, poderá passar essa variável de volta à cadeia por meio de um documento de texto (acho que há outra maneira de fazer isso também, mas isso me escapa).

@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 ########################
    
por 14.07.2014 / 23:33
1

Com relação à primeira parte da pergunta, se você estiver usando tasklist.exe para encontrar um processo, observe que o filtro 'windowtitle' funcionará somente para janelas em um processo separado. Então, se você quiser fazer um arquivo batch detectar o ITSELF (ou seja, verificar se já existe uma instância em execução), use 'imagename':

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

Observe também que, sem um filtro, o processo pode levar muito tempo em versões posteriores do Windows. Aqui está um exemplo de um arquivo em lote que só pode ser executado como uma instância por vez:

@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
    
por 15.09.2017 / 09:30
0

Acabei de concluir o arquivo em lote, que garante que o monitoramento em si, o arquivo em lote e o arquivo de trabalho -facerecog.exe, estejam sendo executados sem problemas. Eu usei hello.exe como uma ferramenta de teste de teste, você pode substituir o seu próprio; ie. ping.exe, etc. Quando ele inicia sem nenhum parâmetro, ele dispara outra instância e sai, para que você possa executá-la como uma tarefa agendada em intervalos específicos. Espero que ajude alguém que não queira usar ferramentas avançadas.

    @goto code

    :comments
    @REM RunMagick for FaceRecog
    @REM Runs FaceRecog forever
    @REM Author Emin Akbulut [email protected]
    @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

Obrigado stijn por seu ótimo comentário. Obrigado surfasb por seu aviso sobre a tarefa agendada que já está em execução.

    
por 29.11.2011 / 15:24