batch file多任务问题与父项共享variables

我试图在x265批量编码video文件,并希望拿出一个batch file,可以自动化的过程。 为了加快速度,我发现有3个ffmpeg调用2个线程会导致理想的编码时间,但是我昨天整天试图想出一个方法来获得一个batch file,它将调用3个实例,然后当他们完成时调用新的。 到目前为止,这是我的位置:

家长分组

@echo off Setlocal EnableDelayedExpansion SET /A COUNT=0 for %%a in (*.mkv) do ( CALL :CHECK SET /A COUNT+=1 START CALL "child.bat" "%%a" ) EXIT /B 0 :CHECK IF !COUNT! EQU 3 ( TIMEOUT /T 5 GOTO :CHECK ) EXIT /B 0 

孩子批次

 ffmpeg <COMMAND LINE ARGS> SET /A COUNT-=1 EXIT /B 0 

我有两个问题。 1)COUNTvariables在父进程中没有被更新,并且在subprocess完成时从不产生新的实例。 2)subprocess不干净退出。 它会使用DOS提示符打开单独的cmd.exe窗口。

有任何想法吗?

编辑:replace嵌套的GOTO,以防止FOR循环破坏

下面的解决方法

 Setlocal EnableDelayedExpansion SET /A COUNT=0 for %%a in (*.mkv) do ( IF !COUNT! EQU 3 CALL :CHECK SET /A COUNT+=1 START CALL "child.bat" "%%a" ) EXIT /B 0 :CHECK IF EXIST DONE.TXT ( SET /A COUNT-=1 DEL DONE.TXT EXIT /B 0 ) ELSE ( TIMEOUT /T 5 GOTO :CHECK ) EXIT /B 0 

孩子批次

 ffmpeg <COMMAND LINE ARGS> :DONE IF EXIST DONE.TXT ( TIMEOUT /T 1 GOTO :DONE ) echo 1 >> DONE.TXT EXIT 0 

    关于你陈述的问题:

    1)子进程不能修改父进程中的环境变量。 您将需要一个不同的机制来检测孩子何时终止。 另外,正如Squashman在他的评论中所述,一个循环内的GOTO将会中断(终止)循环,这就是为什么在第一个3之后没有启动新的子进程。

    2)您的子窗口不会因为使用EXIT / B而终止。 而是使用退出,窗口将关闭。

    你有一个很长的路要走之前,你有一个工作的解决方案;-)

    也许最大的障碍在于检测子进程何时终止。

    我知道3个策略:

    1)使用TASKLIST与FIND / C结合来计算当前正在运行的ffmpeg进程的数量。 这也许是最简单的解决方案,但它不能区分你的脚本启动的进程和其他机制可能已经启动的进程。

    2)使用文件作为信号。 为每个进程创建一个空文件,然后当进程结束时,删除该文件。 您的主脚本可以通过查找文件来监视哪些进程处于活动状态。 这也很简单,但如果其中一个进程在可以删除文件之前崩溃,则表现不佳。 这使您的系统处于不健康的状态。

    3)我最喜欢的是使用锁定文件。 每个子进程通过重定向来锁定一个文件,当进程终止时(崩溃,正常退出,如何),然后锁被释放。 主进程可以尝试锁定相同的文件。 它知道如果锁定成功,孩子已经终止,否则孩子仍在跑步。 这个策略是最复杂的,它使用了神秘的语法,但我觉得它非常有效。

    我已经在并行执行使用选项3) 的shell进程中实现了一个解决方案。 下面是对你的情况的适应/简化的代码。

    我使用START / B在父窗口中启动每个子进程,并将所有输出重定向到锁文件。 完成后,我输入输出文件,以便看到发生的情况。 我还列出了每个子进程的开始和停止时间。

    你只需要调整3个最重要的环境变量来满足你的需求。 其余的应该是好的去。 但是,如果任何文件名包含! 字符。 这个限制可以删除多一点工作。

    脚本中有大量的文档。 %= COMMENT =%语法是在不使用REM的情况下将注释安全地嵌入到循环中的一种方法。

     @echo off setlocal enableDelayedExpansion :: Define the command that will be run to obtain the list of files to process set listCmd=dir /b /ad *.mkv :: Define the command to run for each file, where "%%F" is an iterated file name from the list :: something like YOUR_COMMAND -i "%%F" set runCmd=ffmpeg [** command arguments go here **] :: Define the maximum number of parallel processes to run. set "maxProc=3" ::--------------------------------------------------------------------------------- :: The remainder of the code should remain constant :: :: Get a unique base lock name for this particular instantiation. :: Incorporate a timestamp from WMIC if possible, but don't fail if :: WMIC not available. Also incorporate a random number. set "lock=" for /f "skip=1 delims=-+ " %%T in ('2^>nul wmic os get localdatetime') do ( set "lock=%%T" goto :break ) :break set "lock=%temp%\lock%lock%_%random%_" :: Initialize the counters set /a "startCount=0, endCount=0" :: Clear any existing end flags for /l %%N in (1 1 %maxProc%) do set "endProc%%N=" :: Launch the commands in a loop set launch=1 for /f "tokens=* delims=:" %%F in ('%listCmd%') do ( if !startCount! lss %maxProc% ( set /a "startCount+=1, nextProc=startCount" ) else ( call :wait ) set cmd!nextProc!=%runCmd% echo ------------------------------------------------------------------------------- echo !time! - proc!nextProc!: starting %runCmd% 2>nul del %lock%!nextProc! %= Redirect the lock handle to the lock file. The CMD process will =% %= maintain an exclusive lock on the lock file until the process ends. =% start /b "" cmd /c >"%lock%!nextProc!" 2^>^&1 %runCmd% ) set "launch=" :wait :: Wait for procs to finish in a loop :: If still launching then return as soon as a proc ends :: else wait for all procs to finish :: redirect stderr to null to suppress any error message if redirection :: within the loop fails. for /l %%N in (1 1 %startCount%) do 2>nul ( %= Redirect an unused file handle to the lock file. If the process is =% %= still running then redirection will fail and the IF body will not run =% if not defined endProc%%N if exist "%lock%%%N" 9>>"%lock%%%N" ( %= Made it inside the IF body so the process must have finished =% echo =============================================================================== echo !time! - proc%%N: finished !cmd%%N! type "%lock%%%N" if defined launch ( set nextProc=%%N exit /b ) set /a "endCount+=1, endProc%%N=1" ) ) if %endCount% lss %startCount% ( timeout /t 1 /nobreak >nul goto :wait ) 2>nul del %lock%* echo =============================================================================== echo Thats all folks