Làm cách nào tôi có thể ngăn chạy cùng một tệp bó hai lần để chỉ cho phép một phiên bản?


5

Tôi cần phát hiện nếu một tệp bó đã chạy hay chưa.

Trong đợt tôi chỉ đơn giản lặp và lặp đi lặp lại một số nhiệm vụ. Đôi khi vòng lặp không thể hoàn thành. Tôi chạy tập tin hàng loạt tại một thời điểm theo lịch trình. Khi nó chạy, nó sẽ kiểm tra xem một cá thể trước đó có tồn tại và khỏe mạnh không. Nếu nó không tồn tại, nó sẽ bắt đầu một thể hiện mới. Nếu cá thể tồn tại không lành mạnh / lỗi thời, thì cá thể mới sẽ giết / hủy quá trình cũ hơn.

Một vài lý thuyết:

  1. Làm thế nào tôi có thể phát hiện một trường hợp trước đó trong một tệp bó? Tôi có thể cung cấp một ID như điều?
  2. Tôi có thể sử dụng một cơ chế xung để nói rằng lô là lành; tức là một biến toàn cục, một tệp giả mới được tạo, dấu thời gian trong tệp giả, v.v.
  3. Tôi có thể phát hiện một loại "thời gian chờ" không, nếu xung cuối cùng hết hạn thì cho phép một trường hợp mới ...

Có, tất cả tôi đều muốn chúng trong một tệp bó - thay vì ứng dụng Windows - Tôi nghĩ rằng không khó cho một người nghiện tệp bó và đặt câu hỏi này làm thách thức!


bạn có kiểm tra câu trả lời của tôi ở đây không: superuser.com/questions/362164/ từ
stijn

Vâng cảm ơn. Tôi cần nhiều hơn là nhận ra lô đang chạy; I E. làm thế nào về nhiều trường hợp? Tôi có thể cho họ một cái tên? Để giết chúng riêng lẻ? Tôi không muốn làm thủ công từ Trình quản lý tác vụ.
Đám mây Nime

không chắc chắn ý của bạn - câu hỏi của bạn là về việc cho phép một trường hợp duy nhất. Nhưng bây giờ bạn đang hỏi về nhiều trường hợp? dù sao thì việc giết chúng có thể được thực hiện bằng cách sử dụng taskkill hoặc pskill
stijn

Ví dụ đơn # 1: Monitor.bat domain1.com Ví dụ đơn # 2:
Monitor.bat

đó không phải là một phiên bản duy nhất của tệp bó .. Bạn vẫn có thể sử dụng thủ thuật tiêu đề nếu bạn đặt tiêu đề thành mybatch domain1.com mybatch domeain2.comv.v. Hoặc dùng đến các tệp văn bản mà bạn giữ một danh sách những thứ tốt hơn trong thời gian dài.
stijn

Câu trả lời:


6

Điều này khá khó khăn từ quan điểm tập tin bó, chủ yếu là do không có phương thức được bảo đảm cho một tập tin hàng loạt để phát hiện các trường hợp trên các quy trình.

IMO, tôi nghĩ rằng bạn đang sử dụng công cụ sai. Trong khi, vâng, đó là một thách thức, mối quan tâm quan trọng hơn là "Nó có hoạt động không?"

Tôi chạy tập tin hàng loạt trong một thời gian theo lịch trình.

Điều này cho tôi biết bạn nên sử dụng một chương trình lập lịch, như Lập lịch tác vụ. Trình lập lịch tác vụ sẽ đảm bảo chỉ có một phiên bản tệp của bạn, ngay cả khi nó đang chạy trên người dùng khác.

Ví dụ: nếu bạn chạy tác vụ cứ sau 30 phút, bạn định cấu hình tác vụ để chạy trong thời gian 30 phút. Sau đó, bạn đặt cài đặt "nếu tác vụ đang chạy" thành "không tạo phiên bản mới." Hoặc bạn phải tùy chọn "giết cái cũ", "Chạy phiên bản khác" hoặc "Bắt đầu một nhiệm vụ mới ngay khi hoàn thành nhiệm vụ cũ".

Dành cho Vista trở lên: http://technet.microsoft.com/en-us/l Library / cc748993.aspx#BKMK_cmd
Dành cho Xp trở lên: http://technet.microsoft.com/en-us/l Library / bb490996.aspx

Phương pháp đầy thách thức là tạo ra các trạng thái.

  1. Tệp bó của bạn vào thực thi
  2. Vòng lặp tập tin của bạn
  3. Tập tin lô của bạn kết thúc

Bạn có thể tạo một tệp tùy ý trong% allusersprofile%, như >>Startmybatchfile.txt echo 1đối với từng trạng thái, sau đó kiểm tra xem nó có tồn tại không. Nó cũng sẽ giúp đỡ với rắc rối chụp. Nhưng đừng xóa tập tin ở cuối. Chỉ cần thay đổi nó thành End State. Theo cách đó, nó có tính quyết định nếu tệp bó của bạn đã thực sự kết thúc và không còn chạy.

Tôi sẽ không sử dụng biến toàn cục vì nó yêu cầu quyền quản trị viên để chạy.

Bạn cũng có thể sử dụng biến% temp% nhưng thay đổi từ người dùng sang người dùng. Tôi đoán đó không phải là vấn đề đáng lo ngại nếu bạn là một người dùng đăng nhập.


Nếu tất cả các trường hợp bị chấm dứt thì TS bắt đầu một thể hiện mới. Đó là lý do tại sao tôi sử dụng TS. Ngoài ra, khi hệ điều hành khởi động, TS tạo phiên bản đầu tiên, v.v.
Nime Cloud

5

Đây là kịch bản mới nhất và đáng tin cậy nhất của tôi:

: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

1
Điều gì đang xảy ra trong kịch bản này? Chúng ta có thể sử dụng nó trong nhiều tập lệnh không?
Angelina

3

Cách tốt nhất tôi tìm thấy để làm điều này là đặt tiêu đề trên một tập lệnh bó.

Từ đó bạn có thể sử dụng danh sách tác vụ để truy vấn số lượng các tiến trình đang chạy theo tên tiêu đề. Đoạn mã dưới đây bất cứ điều gì bên dưới process_count = 2 có nghĩa là quá trình không chạy, 3 có nghĩa là chính xác 1 trường hợp đang chạy và nếu trên 3, có hơn 3 quy trình đang chạy.

Hackish, nhưng hàng loạt là những gì nó được.

Đối với tập lệnh Looping liên tục, tôi không đủ thông minh để nghĩ ra cách viết thời gian bắt đầu vào trường tiêu đề và truy vấn từ tập lệnh bó khác. Tôi đã nghĩ đến việc sao chép và đổi tên tên tệp với thời gian thực hiện điều đó, nhưng với tôi bất cứ điều gì ngoại trừ một truy vấn trực tiếp thì lộn xộn hơn giá trị của nó.

Vâng tôi có thể thả vào tập tin văn bản, nhưng tôi lo lắng về hỏng bộ nhớ 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 ########################

Ngoài ra, cho bất cứ ai quan tâm; Dưới đây là cách tìm ra PID của quy trình hiện đang thực thi. Bây giờ nếu bạn chuyển một biến vào một tập lệnh con, bạn có thể chuyển biến đó sao lưu chuỗi qua tài liệu văn bản (tôi nghĩ có một cách khác để làm điều này nhưng điều đó thoát khỏi tôi).

@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

Liên quan đến phần đầu tiên của câu hỏi, nếu bạn đang sử dụng tasklist.exe để tìm quy trình, hãy lưu ý rằng bộ lọc 'windowtitle' sẽ chỉ hoạt động cho các cửa sổ trong một quy trình riêng biệt. Vì vậy, nếu bạn muốn tạo một tệp bó phát hiện ITSELF (tức là kiểm tra xem đã có phiên bản đang chạy chưa), bạn nên sử dụng 'tên tệp' thay thế:

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

Cũng lưu ý rằng không có bộ lọc, quá trình có thể mất nhiều thời gian trên các phiên bản Windows mới hơn. Dưới đây là một ví dụ về một tệp bó chỉ có thể được chạy như một thể hiện tại một thời điểm:

@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

1

Tôi đã tìm thấy một giải pháp sử dụng các tệp văn bản để theo dõi tất cả các PID trước đó mà tệp bat đã có. Nó cố gắng giết chúng một cách im lặng và sau đó thêm PID hiện tại vào danh sách sau.

Nếu bạn không muốn nó giết quá trình cũ, đã có sẵn, chỉ cần thay thế dòng có "nhiệm vụ" bằng bất cứ điều gì bạn muốn làm với nó.

(có thể yêu cầu bạn chạy với tư cách quản trị viên để có quyền hủy quy trình trùng lặp. Xem mã độ cao quyền bên dưới để thực hiện tùy chọn nếu bạn không muốn phải chạy với tư cách quản trị viên mỗi lần.)

@echo off
set WorkingDir=%cd% 
if exist MostRecentPID.txt ( del "PIDinfo.txt"  /f /q ) > nul
cd ..\..\..\..\..\..\..
title mycmd
tasklist /v /fo csv | findstr /i "mycmd" > %WorkingDir%\PIDinfo.txt
set /p PIDinfo=<%WorkingDir%\PIDinfo.txt

REM below, the 11 means get substring starting a position 11 with length of 5 characters. The tasklist command gives a long and verbose value so this will get just the PID part of the string.
set PID5chars=%PIDinfo:~11,5%
set PID4chars=%PIDinfo:~11,4%

if exist PreviousPIDs.txt (
    for /F "tokens=*" %%A in (PreviousPIDs.txt) do taskkill.exe /F /T /PID %%A > nul 2>&1
    goto CheckIfFourCharPID
)

:CheckIfFourCharPID
if %PID4chars% gtr 8100 (
    for /F "tokens=*" %%A in (PreviousPIDs.txt) do taskkill.exe /F /T /PID %%A > nul 2>&1
    echo %PID4chars% >> "PreviousPIDs.txt"
) else (
    echo %PID5chars% >> "PreviousPIDs.txt"
)

Giải thích: (cảnh báo: rất kỹ thuật)

Giải pháp này nhận được một chuỗi con của lệnh danh sách tác vụ để chỉ nhận PID. Sẽ không có một PID nào cho cmd.exe lớn hơn 18100, vì vậy hãy kiểm tra xem PID4chars có lớn hơn 8100 hay không để chúng tôi biết đó là số có 4 chữ số hay 5 chữ số

trường hợp 1: một PID gồm 5 chữ số như 17504 có giá trị PID5chars val 17504 và giá trị PID4chars là 1750, vì vậy chúng tôi thêm PID5chars vào các tệp văn bản của PID để tiêu diệt

trường hợp 2: một PID gồm 4 chữ số như 8205 có giá trị PID5chars là 8205 "và giá trị PID4chars là 8205, vì vậy chúng tôi thêm PID4chars vào các tệp văn bản của PID để tiêu diệt

trường hợp 3: một PID gồm 4 chữ số như 4352 có giá trị PID5chars là 4352 "và giá trị PID4chars là 4352, vì vậy chúng tôi thêm PID4chars vào các tệp văn bản của PID để tiêu diệt


MÃ SỐ TẠM DỪNG TÙY CHỌN

(đặt cái này ở đầu tập tin bat của bạn và nó sẽ tự động chạy nó với tư cách quản trị viên.)

    @echo off
    setlocal DisableDelayedExpansion

    set "batchPath=%~0"
    for %%k in (%0) do set batchName=%%~nk
    cd ..\..\..\..\..\..\..\..
    if exist %cd%\Temp (
    set temp=%cd%\Temp
    goto vbsGetPrivileges
    )
    if exist %cd%\Windows\Temp (
    set temp=%cd%\Windows\Temp
    goto vbsGetPrivileges
    )
    set temp=%cd%

    :vbsGetPrivileges
    set "vbsGetPrivileges=%temp%\OEgetPriv_%batchName%.vbs"
    setlocal EnableDelayedExpansion

    :CheckIfRunningAsAdmin
    net session >nul 2>&1
    if %ERRORLEVEL% == 0 ( 
        goto gotPrivileges 
    ) else ( goto ElevatePermissions )

    :ElevatePermissions
    if '%1'=='ELEV' (echo ELEV & shift /1 & goto gotPrivileges)
    ECHO Set UAC = CreateObject^("Shell.Application"^) > "%vbsGetPrivileges%"
    ECHO args = "ELEV " >> "%vbsGetPrivileges%"
    ECHO For Each strArg in WScript.Arguments >> "%vbsGetPrivileges%"
    ECHO args = args ^& strArg ^& " "  >> "%vbsGetPrivileges%"
    ECHO Next >> "%vbsGetPrivileges%"
    ECHO UAC.ShellExecute "!batchPath!", args, "", "runas", 1 >> "%vbsGetPrivileges%"
    "%SystemRoot%\System32\WScript.exe" "%vbsGetPrivileges%" %*
    exit /B

    :gotPrivileges
    setlocal & pushd .
    cd /d %~dp0
    if '%1'=='ELEV' (del "%vbsGetPrivileges%" 1>nul 2>nul  &  shift /1)
    net session >nul 2>&1
    if %ERRORLEVEL% == 0 (
        goto Continue
    ) else (
        REM unable to elevate permissions so tell user to run file as admin manually
        echo Please re-run this file as administrator. Press any key to exit...
        pause > nul
        goto Exit
     )

    :Continue
    <insert rest of code here>

0

Tôi vừa hoàn thành tệp bó, cái nào đảm bảo cả việc giám sát chính nó - tệp bó - và tệp công việc -facerecog.exe - đang chạy trơn tru. Tôi đã sử dụng hello.exe như một công cụ thăm dò thử nghiệm, bạn có thể tự thay thế; I E. ping.exe, v.v. Khi nó khởi động mà không có bất kỳ tham số nào, nó kích hoạt một thể hiện khác và thoát ra, vì vậy bạn có thể chạy nó như một tác vụ theo lịch trình trong các khoảng thời gian cụ thể. Tôi hy vọng nó sẽ giúp được ai đó không muốn sử dụng các công cụ tiên tiến.

    @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

Cảm ơn stijn cho nhận xét tuyệt vời của mình. Cảm ơn Surfasb vì đã cảnh báo về Nhiệm vụ theo lịch trình đã chạy.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.