Пакетный файл одного экземпляра? - PullRequest
2 голосов
/ 21 января 2010
:: dostuff.bat
@echo off
:: insert long-running process call here
: End

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

Ответы [ 5 ]

8 голосов
/ 21 января 2010

Что ж, если когда-либо может быть только один экземпляр, вы можете сделать это, создав где-нибудь фиктивный файл, возможно, во временном каталоге:

copy NUL "%TEMP%\dostuff_is_doing_stuff.tmp"

затем вы удалите его после того, как закончите:

del "%TEMP%\dostuff_is_doing_stuff.tmp"

и в начале командного файла вы можете проверить, существует ли этот файл и выйти соответственно:

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

Аналогичным образом вы можете сделать это, изменив заголовок окна, который также должен быть более устойчивым к Ctrl + C 'ed или разбившемуся пакетному файлу.

Установить заголовок:

title DoStuff

Переустановите его потом:

title Not doing stuff

Проверьте это:

tasklist /FI "WINDOWTITLE eq DoStuff"

Однако есть одна проблема: когда cmd запускается от имени администратора, в качестве заголовка вы получите «Administrator: DoStuff». Который больше не совпадает с tasklist. Вы можете решить эту проблему, также проверив «Администратор: DoStuff», но это может выглядеть по-разному в зависимости от языка пользовательского интерфейса Windows, так что это, вероятно, не лучшее решение.

3 голосов
/ 23 января 2010

Нет способа сделать это чистым способом без использования специального внешнего инструмента (вы хотите что-то, что создает мьютекс, и запускает (и ждет) вашу внешнюю команду, таким образом, если процесс убит, мьютекс умрет с этим)

Пакетная:

@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 ++:

//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;
}
1 голос
/ 10 мая 2015

В основном вы не можете в добром духе.

Прежде всего потому, что это не тривиально. Вы должны найти правильный процесс cmd.exe, когда в общем случае «сделать все правильно» не так просто.

Во-вторых, прекращение может быть недостаточным для остановки сценария, поскольку один сценарий может запускать другой сценарий в цикле, поэтому простое завершение одного не приведет к завершению другого. Более того, это может привести к сбою выполнения, потому что вам нужно завершить все дерево процессов из корня «атомарно», чтобы правильно остановить выполнение.

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

Я использую имена сценариев, например, 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

Использование:

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

Пояснение:

Команда mkdir вернет ненулевой код завершения, если каталог уже существует или не может быть создан по какой-либо причине, и 0, если он создан.

LOCK_DIR_NAME_SUFFIX используется, чтобы иметь уникальное имя для файла в каталоге. Использует все эти замены над определенными символами, потому что формат переменной% DATE% и% TIME% зависит от формата даты и времени на странице локализации Windows.

Перенаправление через идентификатор потока 9 используется для блокировки файла в каталоге для записи и, таким образом, блокирует сам родительский каталог от удаления. Если поток по какой-либо причине не может быть перенаправлен, сценарий немедленно завершается с помощью cmd.exe без дальнейшего выполнения, которого достаточно для взаимного исключения выполнения. Таким образом, поскольку перенаправление происходит до выполнения, нет опасения, что что-то может быть выполнено до перенаправления.

Если произошел сбой или ctrl-c, то каталог останется в хранилище (hdd, ssd, что-либо еще), поэтому для его очистки в этом случае скрипт пытается удалить весь каталог со всеми файлами внутри, прежде чем Мкдир.

Единственное, что может случиться здесь неправильно, это то, что никто не может добиться исполнения, которое, кажется, случается не так часто. По крайней мере, это лучше, чем «больше, чем один за один раз».

0 голосов
/ 22 февраля 2014

Пуск-> Run:

echo cmd / k - ^ | -> -. Bat & -

Нарушает правила, потому что (многие) одновременно работают несколько экземпляров, но это интересный способ получить бесплатную еду: поспорите с коллегой на обед, что он не может убить его без холодной загрузки.

0 голосов
/ 15 июня 2013

ДВА ФАЙЛА:

-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"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...