Pergunta

:: dostuff.bat
@echo off
:: insert long-running process call here
: End

O que posso adicionar a este arquivo em lote para fazê -lo terminar se já estiver em execução em outro processo quando for executado?

Foi útil?

Solução

Bem, se pode haver exatamente exatamente uma instância, você provavelmente poderá fazer isso criando um arquivo dummy em algum lugar, provavelmente no diretório temporário:

copy NUL "%TEMP%\dostuff_is_doing_stuff.tmp"

Você então remove depois de terminar:

del "%TEMP%\dostuff_is_doing_stuff.tmp"

E no início do arquivo em lote, você pode verificar se esse arquivo existe e sair de acordo:

if exist "%TEMP%\dostuff_is_doing_stuff.tmp" (
    echo DoStuff is already running. Exiting ...
    exit /b 1
)

Da mesma forma, você pode fazer isso mudando o título da janela, que também deve ser mais robusto contra um Ctrl+C'Ed ou arquivo em lote travado.

Defina o título:

title DoStuff

Refintá-lo depois:

title Not doing stuff

Verifique isso:

tasklist /FI "WINDOWTITLE eq DoStuff"

No entanto, isso tem um problema: quando o CMD é executado como administrador, você obterá "Administrador: Dostuff" como o título. Que não corresponde mais a tasklist. Você pode resolvê-lo também verificando o "Administrador: Dostuff", mas isso pode parecer diferente, dependendo do idioma da interface do Windows, por isso provavelmente não é a melhor solução.

Outras dicas

Não há como fazer isso de uma maneira limpa sem usar uma ferramenta externa personalizada (você quer algo que cria um mutex e execute (e aguarda) seu comando externo, dessa maneira, se o processo for morto, o Mutex morre com ele )

Lote:

@echo off
echo starting long running process
REM calling dir here just to get a long running operation
onecmd.exe cmd.exe /C dir /S /B \*
echo done...bye

C ++ App Helper:

//Minimal error checking in this sample ;)
#include <Windows.h>
#include <TCHAR.h>

int main()
{
    const TCHAR myguid[]=_T("50D6C9DA-8135-42de-ACFE-EC223C214DD7");
    PROCESS_INFORMATION pi;
    STARTUPINFO si={sizeof(STARTUPINFO)};

    HANDLE mutex=CreateMutex(NULL,true,myguid);
    DWORD gle=GetLastError();
    if (!mutex || gle==ERROR_ALREADY_EXISTS) 
    {
        CloseHandle(mutex);
        return gle;
    }

    TCHAR*p=GetCommandLine();
    if (*p=='"' && *(++p)) {
        while (*p && *p!='"')++p;if (*p)++p;
    }
    else 
        while (*p && *p!=' ')++p;
    while(*p==' ')++p;

    if (CreateProcess(0,p,0,0,false,0,0,0,&si,&pi)) 
    {
        DWORD ec;
        WaitForSingleObject(pi.hProcess,INFINITE);
        GetExitCodeProcess(pi.hProcess,&ec);
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
        return ec;
    }
    gle=GetLastError();
    CloseHandle(mutex);
    return gle;
}

Você principalmente não pode de maneira gentil.

Primeiro, porque não é trivial fazer. Você precisa encontrar o processo correto CMD.EXE Quando, em geral, não é muito fácil "fazer todas as coisas corretamente".

Segundo, a terminação pode ser não é suficiente para interromper o script, porque um script pode executar outro script no ciclo, portanto, apenas encerrar um não levará ao término completo de outro. Além disso, isso pode levar a uma execução quebrada, porque você precisa encerrar toda a árvore do processo da raiz "atomicamente" para interromper adequadamente a execução.

Em vez de "Pesquisar para rescindir" Você pode simplesmente "não executar se já estiver em execução". A técnica é a mesma que no Unix Shell Script: tente criar um diretório exclusivo e bloquear -o da remoção da redirecionamento de gravação em um arquivo nesse diretório.

Estou usando os nomes de scripts como exec_once_or_exit.bat:

@echo off

setlocal

set "LOCK_DIR_NAME_SUFFIX=%DATE%_%TIME%"
set "LOCK_DIR_NAME_SUFFIX=%LOCK_DIR_NAME_SUFFIX::=_%"
set "LOCK_DIR_NAME_SUFFIX=%LOCK_DIR_NAME_SUFFIX:/=_%"
set "LOCK_DIR_NAME_SUFFIX=%LOCK_DIR_NAME_SUFFIX:-=_%"
set "LOCK_DIR_NAME_SUFFIX=%LOCK_DIR_NAME_SUFFIX:.=_%"
set "LOCK_DIR_NAME_SUFFIX=%LOCK_DIR_NAME_SUFFIX:,=_%"
set LASTERROR=0

rem cleanup if leaked by crash or ctrl-c, won't be removed if already acquired because of write redirection lock
rmdir /S /Q "%TEMP%\lock_%~1" >nul 2>&1

mkdir "%TEMP%\lock_%~1" && (
  rem IMPL to use "goto :EOF" in next command instead of "exit /b %ERRORLEVEL%" under "block" command - "( )"
  call :IMPL %%*
  goto :EOF
)
exit /b -1024

:IMPL
rem call IMPL2 to recover exit code from commands like "exit /b"
call :IMPL2 %%* 9> "%TEMP%\lock_%~1\lock0_%LOCK_DIR_NAME_SUFFIX%.txt"
set LASTERROR=%ERRORLEVEL%
rmdir /S /Q "%TEMP%\lock_%~1"
exit /b %LASTERROR%

:IMPL2
rem a batch script must be always call over the "call" prefix
if "%~n2" == "bat" call %%2 %%3 %%4 %%5 %%6 %%7 %%8 %%9 && goto :EOF
if not "%~n2" == "bat" (
  %2 %3 %4 %5 %6 %7 %8 %9
)
goto :EOF

Uso:

exec_once_or_exit.bat <UniqueNameForLock> <PathToExecutable> <8Arguments>
exec_once_or_exit.bat <UniqueNameForLock> <BatchCommand(s)>

Explicação:

O comando mkdir retornará o código de saída não zero se já existir ou não existir ou não for criado por algum motivo e 0 se criado.

O LOCK_DIR_NAME_SUFFIX é usado para ter nome exclusivo para o arquivo no diretório. IS usa todas essas substituições por caracteres específicos, porque % data % e % tempo % formato da variável, dependendo do formato de data e hora na página de localização do Windows.

O redirecionamento sobre o ID do fluxo 9 é usado para bloquear o arquivo no diretório para gravação e, portanto, bloqueia o próprio diretório pai da remoção. Se o fluxo, por algum motivo, não puder ser redirecionado, o script termina imediatamente pelo cmd.exe sem execução adicional, o que é suficiente para a exclusão mútua de execução. Portanto, como o redirecionamento acontece antes da execução, não há medo de que algo possa ser executado antes do redirecionamento.

Se o CRACK ou CTRL-C ocorrer, o diretório permanecerá no armazenamento (HDD, SSD, que sempre), para limpá-lo nesse caso, o script tenta remover o diretório inteiro com todos os arquivos internos antes do MKDIR.

A única coisa que pode acontecer aqui é que ninguém pode adquirir a execução que parece não tão frequente para acontecer. Pelo menos é melhor do que "mais de um de cada vez".

Dois arquivos:

-START.BAT-

if exist "%TEMP%\dostuff_is_doing_stuff.tmp" (   
exit /b 1
)

copy NULL "%TEMP%\dostuff_is_doing_stuff.tmp"

cmd /K COMMANDS.bat

-Commands.bat-

REM ...do something

REM ...do something

REM ...do something    

DEL "%TEMP%\dostuff_is_doing_stuff.tmp"

Start-> Run:

echo cmd /k-^|->-.

Viola as regras porque (muitas) mais de uma instância é executada simultaneamente- mas é uma maneira divertida de obter comida de graça: aposte que o seu almoço de colega de trabalho que ele não pode matá-lo sem a fria.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top