2010-01-21 4 views

ответ

8

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

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

+0

Идея списка задач великолепна. Спасибо. Как написать тест на выход этой команды? То есть, когда я запускаю список задач в batchfile, какая логика знать, сообщила ли она мне, что пакетный файл уже запущен или нет? – lance

+0

Хм, извините за этот список задач. Я думал, что tasklist ответит кодом выхода; вы могли бы использовать их с 'if errorlevel ...', но tasklist всегда возвращает 0, независимо от того, было ли обнаружено окно или нет. Это отстой. Вы все равно можете записать вывод с помощью 'for/f', но я бы посоветовал ему, так как это по своей сути хрупкое (локализация и прочее). – Joey

+0

Справа. Когда это не сработало, я уволил свой вопрос. Я могу передать результаты списка задач в findstr, который возвращает условный уровень ошибок. Мне это не нравится, но я никогда не занимаюсь эстетикой, когда я создаю пакетные файлы (особенно для личного использования, как в этом случае). – lance

3

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

Batch:

@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; 
} 
0

ДВА ФАЙЛА:

-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" 
0

Пуск-> Выполнить:.

эхо CMD/к -^| -> - летучая мышь & -

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

1

В основном вы не можете на виду.

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

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

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

я использую имена сценариев что-то вроде 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.

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

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

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