Как закодировать счетчик для ожидающих процессов в пакетном файле?
Я хотел бы показать пользователю spinner, что-то сделано в фоновом режиме, но не знаю, как это работает в пакетном файле.
Есть ли у кого подсказка?
Ответы
Ответ 1
На самом деле это можно сделать довольно просто с помощью чистых собственных команд, вам просто нужно знать, как использовать более сложные из них. Нет необходимости использовать внешние инструменты, такие как VBScript или неприятные побочные эффекты, такие как очистка экрана.
То, что вы ищете, эквивалентно команде bash "echo -n
", которая выводит строку без новой строки. В пакете XP это достигается с помощью "set /p
" (запросить у пользователя ответ с подсказкой) с пустым вводом следующим образом:
<nul (set /p junk=Hello)
echo. again.
выведет строку "Привет снова". без промежуточной строки.
Этот трюк (и использование CTRL-H, символ backspace можно увидеть в следующем тесте script, который запускает (один за другим) 10-секундную подзадачу с 20-секундным таймаутом и 15-секундная подзадача с 10-секундным таймаутом.
Полезная нагрузка script создается фактическим запуском script, и его единственным требованием является то, что он выполняет свою работу, а затем удаляет файл флага по окончании, так что функция монитора сможет его обнаружить.
Имейте в виду, что строки ^ H в этом script являются фактически символами CTRL-H, ^ | это два отдельных символа, используемых для выхода из символа трубы.
@echo off
:: Localise environment.
setlocal enableextensions enabledelayedexpansion
:: Specify directories. Your current working directory is used
:: to create temporary files tmp_*.*
set wkdir=%~dp0%
set wkdir=%wkdir:~0,-1%
:: First pass, 10-second task with 20-second timeout.
del "%wkdir%\tmp_*.*" 2>nul
echo >>"%wkdir%\tmp_payload.cmd" ping 127.0.0.1 -n 11 ^>nul
echo >>"%wkdir%\tmp_payload.cmd" del "%wkdir%\tmp_payload.flg"
call :monitor "%wkdir%\tmp_payload.cmd" "%wkdir%\tmp_payload.flg" 20
:: Second pass, 15-second task with 10-second timeout.
del "%wkdir%\tmp_*.*" 2>nul:
echo >>"%wkdir%\tmp_payload.cmd" ping 127.0.0.1 -n 16 ^>nul
echo >>"%wkdir%\tmp_payload.cmd" del "%wkdir%\tmp_payload.flg"
call :monitor "%wkdir%\tmp_payload.cmd" "%wkdir%\tmp_payload.flg" 10
goto :final
:monitor
:: Create flag file and start the payload minimized.
echo >>%2 dummy
start /min cmd.exe /c "%1"
:: Start monitoring.
:: i is the indicator (0=|,1=/,2=-,3=\).
:: m is the number of seconds left before timeout.
set i=0
set m=%3
<nul (set /p z=Waiting for child to finish: ^|)
:: Loop here awaiting completion.
:loop
:: Wait one second.
ping 127.0.0.1 -n 2 >nul
:: Update counters and output progress indicator.
set /a "i = i + 1"
set /a "m = m - 1"
if %i% equ 4 set i=0
if %i% equ 0 <nul (set /p z=^H^|)
if %i% equ 1 <nul (set /p z=^H/)
if %i% equ 2 <nul (set /p z=^H-)
if %i% equ 3 <nul (set /p z=^H\)
:: End conditions, complete or timeout.
if not exist %2 (
echo.
echo. Complete.
goto :final
)
if %m% leq 0 (
echo.
echo. *** ERROR: Timed-out waiting for child.
goto :final
)
goto :loop
:final
endlocal
Ответ 2
Попробуйте следующее:
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
CALL :BACKSPACE $BS
SET /A FULL_COUNT=60
SET /A MAX_COUNT=160
SET /A Spin_Delay=50
SET "_MSG=Process running..."
SET /A CTR=0
SET /A TCT=0
IF NOT [%1]==[] SET _MSG=%~1
IF NOT [%2]==[] SET /A FULL_COUNT=%2
IF NOT [%3]==[] SET /A SPIN_DELAY=%3
IF %FULL_COUNT% GTR %MAX_COUNT% SET FULL_COUNT=%MAX_COUNT%
(SET/P=%_MSG%*)<nul
FOR /L %%A IN (1,1,%FULL_COUNT%) DO (
CALL :DELAY %SPIN_DELAY%
IF !CTR! EQU 0 (set/p=%$BS%³)<nul
IF !CTR! EQU 1 (set/p=%$BS%/)<nul
IF !CTR! EQU 2 (set/p=%$BS%Ä)<nul
IF !CTR! EQU 3 (set/p=%$BS%\)<nul
SET /A CTR=%%A %% 4
)
(SET/P=%$BS%*)<nul
ENDLOCAL & EXIT /B %CTR%
:BackSpace
setlocal
for /f %%a in ('"prompt $H$S &echo on &for %%b in (1) do rem"') do set "Bs=%%a"
endlocal&call set %~1=%BS%&exit /b 0
:Delay msec
setlocal enableextensions
set/a correct=0
set/a msecs=%1+5
if /i %msecs% leq 20 set /a correct-=2
set time1=%time: =%
set/a tsecs=%1/1000 2>nul
set/a msecs=(%msecs% %% 1000)/10
for /f "tokens=1-4 delims=:." %%a in ("%time1%") do (
set hour1=%%a&set min1=%%b&set sec1=%%c&set "mil1=%%d"
)
if /i %hour1:~0,1% equ 0 if /i "%hour1:~1%" neq "" set hour1=%hour1:~1%
if /i %min1:~0,1% equ 0 set min1=%min1:~1%
if /i %sec1:~0,1% equ 0 set sec1=%sec1:~1%
if /i %mil1:~0,1% equ 0 set mil1=%mil1:~1%
set/a sec1+=(%hour1%*3600)+(%min1%*60)
set/a msecs+=%mil1%
set/a tsecs+=(%sec1%+%msecs%/100)
set/a msecs=%msecs% %% 100
:: check for midnight crossing
if /i %tsecs% geq 86400 set /a tsecs-=86400
set/a hour2=%tsecs% / 3600
set/a min2=(%tsecs%-(%hour2%*3600)) / 60
set/a sec2=(%tsecs%-(%hour2%*3600)) %% 60
set/a err=%msecs%
if /i %msecs% neq 0 set /a msecs+=%correct%
if /i 1%msecs% lss 20 set msecs=0%msecs%
if /i 1%min2% lss 20 set min2=0%min2%
if /i 1%sec2% lss 20 set sec2=0%sec2%
set time2=%hour2%:%min2%:%sec2%.%msecs%
:wait
set timen=%time: =%
if /i %timen% geq %time2% goto :end
goto :wait
:end
for /f "tokens=2 delims=." %%a in ("%timen%") do set num=%%a
if /i %num:~0,1% equ 0 set num=%num:~1%
set/a err=(%num%-%err%)*10
endlocal&exit /b %err%
Ответ 3
Если вы не против очистки экрана... попробуйте следующее:
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET COUNT=1
START CALC
:BEGIN
CLS
IF !COUNT! EQU 1 ECHO \
IF !COUNT! EQU 2 ECHO -
IF !COUNT! EQU 3 ECHO /
IF !COUNT! EQU 4 ECHO -
IF !COUNT! EQU 4 (
SET COUNT=1
) ELSE (
SET /A COUNT+=1
)
PSLIST CALC >nul 2>&1
IF %ERRORLEVEL% EQU 1 GOTO END
GOTO BEGIN
:END
EDIT: этот образец запустит калькулятор, а затем отобразит "счетчик", пока вы не закроете калькулятор. Я использую pslist для проверки наличия CALC.EXE. > nul 2 > & 1 перенаправляет STDOUT и STDERR в nul, поэтому ничто из PSLIST не будет отображаться.
Ответ 4
Если я понимаю ваш вопрос, который вы хотите использовать, поскольку какая-то операция, которую вы выполняете, занимает много времени, и вы хотите показать пользователю, что что-то происходит, правильно?
В этом случае, насколько я знаю, это невозможно с помощью собственных команд. (это было бы возможно, если бы у вас была программа, которая показывала spinner при выполнении операции, которая занимает много времени)
И похоже, что эхо не поддерживает ansi escape-последовательности (в старые времена вы должны были загрузить ansi.sys, не знаете, существует ли это еще), поэтому вы не можете использовать ansi для управления курсором.
Ответ 5
Если вы имеете в виду пакет Windows script, вы не можете сделать это изначально. Оператор echo, используемый для печати на консоли, всегда печатает новую строку, и вы не можете перемещать курсор.
Это немного взломать, но вы можете сделать это с помощью комбинации VBScript и пакета script.
Этот VBScript напечатает обратное пространство, затем его аргумент:
WScript.StdOut.Write(chr(8) & WScript.Arguments(0))
Поместите это в файл vbsEcho.vbs
, затем вызовите этот script из своей партии script. Следующая партия script будет отображать счетчик, пока не нажмете CTRL-C:
@echo off
:LOOP
cscript //nologo vbsEcho.vbs "\"
cscript //nologo vbsEcho.vbs "|"
cscript //nologo vbsEcho.vbs "/"
cscript //nologo vbsEcho.vbs "-"
goto :LOOP
EDIT: Используя некоторые идеи из ответа aphoria, этот script запустит калькулятор Windows и покажет счетчик, пока калькулятор не закроет:
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET COUNT=1
START CALC
cscript //nologo vbsEcho.vbs "Calculating: \"
:LOOP
IF !COUNT! EQU 1 cscript //nologo vbsEcho.vbs "|"
IF !COUNT! EQU 2 cscript //nologo vbsEcho.vbs "/"
IF !COUNT! EQU 3 cscript //nologo vbsEcho.vbs "-"
IF !COUNT! EQU 4 (
cscript //nologo vbsEcho.vbs "\"
set COUNT=1
) else (
set /a COUNT+=1
)
pslist CALC >nul 2>&1
if %ERRORLEVEL% EQU 1 goto :end
goto :LOOP
:END
cscript //nologo vbsEcho.vbs ". Done."
Ответ 6
В spatch CAN будет выполняться пакетный script, вам понадобятся только некоторые переменные:
@echo off
:spinner
set mSpinner=%mSpinner%.
if %mSpinner%'==..............................' set mSpinner=.
cls
echo %mSpinner%
rem Check if the process has finished via WMIC and/or tasklist.
goto spinner
:exit
Для самой BAT для обнаружения процесса, выполняющегося/выходящего. Вы можете сделать это с помощью интерфейса командной строки WMI или команды tasklist, из которой у меня ограниченные знания.
Если бы он вернулся в DOS-дни, вы могли бы даже сделать это без очистки экрана... не используя некоторую комбинацию escape-символов. Я не знаю, возможно ли это на Vista/XP.
Ответ 7
Вы можете использовать счетчик, который печатает другой символ из заданного набора (например, "\ |/-" ), и вы меняете символ в соответствии с "counter modulo 4". В любом случае, вы не говорите, на каком языке вы работаете, поэтому немного сложно быть более точным.
EDIT: теперь, когда мы знаем, в какой среде вы играете, я бы сказал, что язык BAT/CMD на самом деле не соответствует задаче... Я бы рекомендовал любой язык сценариев, Ruby был моим любимым.
Ответ 8
Я считаю, что самый простой способ - обновить заголовок - таким образом, вам не нужно делать CLS все время.
Причиной для двух строк ping -n является то, что быстрее для ping выполнить двойной пинг секунды каждый против одного пинга в две секунды.
Кроме того, для тех, кто не знает, a:: такой же, как REM, за исключением того, что комментарии игнорируются в начале анализатора (я думаю, это правильное слово), а не в конце. Проще говоря, эта строка игнорируется.
:: begin spin.cmd
@echo off
setlocal
set COUNT=0
set MAXCOUNT=10
set SECONDS=1
:LOOP
title "\"
call :WAIT
title "|"
call :WAIT
title "/"
call :WAIT
title "-"
if /i "%COUNT%" equ "%MAXCOUNT%" goto :EXIT
set /a count+=1
echo %COUNT%
goto :LOOP
:WAIT
ping -n %SECONDS% 127.0.0.1 > nul
ping -n %SECONDS% 127.0.0.1 > nul
goto :EOF
:EXIT
title FIN!
endlocal
:: end spin.cmd
Ответ 9
paxdiablos имеет удивительный ответ, но необходимость повторять ваши команды в файл полезной нагрузки раздражает. Трудно читать и трудно отлаживать. Я взял его код и немного изменил его для своего использования:
@echo off
:: Localise environment.
setlocal enableextensions enabledelayedexpansion
set wkdir=%~dp0%
set wkdir=%wkdir:~0,-1%
set done_flag="%wkdir%\tmp_payload.flg"
set timeout=7
:controller
IF (%1)==() (
call :monitor step1 "Getting stuff from SourceSafe: "
call :monitor step2 "Compiling some PHP stuff: "
call :monitor step3 "Finishing up the rest: "
) ELSE ( goto %1 )
goto final
:step1
::ping for 5 seconds
ping 127.0.0.1 -n 6 >nul
del "%wkdir%\tmp_payload.flg"
goto final
:step2
::ping for 10 seconds
ping 127.0.0.1 -n 11 >nul
del "%wkdir%\tmp_payload.flg"
goto final
:step3
::ping for 5 seconds
ping 127.0.0.1 -n 6 >nul
del "%wkdir%\tmp_payload.flg"
goto final
:monitor
:: Create flag file and start the payload minimized.
:: echo the word "dummy" to the flag file (second parameter)
echo >>%done_flag% dummy
:: start the command defined in the first parameter
start /min cmd.exe /c "test2.bat %1"
:: Start monitoring.
:: i is the indicator (0=|,1=/,2=-,3=\).
:: m is the number of seconds left before timeout.
set i=0
set m=%timeout%
set str=%2
for /f "useback tokens=*" %%a in ('%str%') do set str=%%~a
<nul (set /p z=%str%^|)
:: Loop here awaiting completion.
:loop
:: Wait one second.
ping 127.0.0.1 -n 2 >nul
:: Update counters and output progress indicator.
set /a "i = i + 1"
set /a "m = m - 1"
if %i% equ 4 set i=0
if %i% equ 0 <nul (set /p z=^|)
if %i% equ 1 <nul (set /p z=/)
if %i% equ 2 <nul (set /p z=-)
if %i% equ 3 <nul (set /p z=\)
:: End conditions, complete or timeout.
if not exist %done_flag% (
::echo.
echo Complete
goto :final
)
if %m% leq 0 (
echo.
echo. *** ERROR: Timed-out waiting for child.
goto :final
)
goto :loop
:final
endlocal
Ответ 10
Эта процедура проверяет вывод списка задач для процесса, который вы начинаете с cmd.
Передайте ему имя exe в качестве параметра, например.
call: spinner calc.exe
Он сообщает
Истек: 001 секунда
и увеличивает секунды до тех пор, пока процесс exe не завершится.
Сообщение Elapsed 001 seconds перезаписывается каждую секунду ECHO.exe -n\r
который эхосигнал cr без подачи линии.
Echo.exe доступен по адресу
http://www.paulsadowski.com/wsh/cmdprogs.htm
@echo off
start calc
call :spinner calc.exe
pause
:spinner
SET COUNT=1
:BEGIN
set "formattedValue=000000%count%"
ECHO.exe -n Elapsed: %formattedValue:~-3% seconds
ECHO.exe -n \r %= -n (suppress crlf) \r output a cr =%
SET /A COUNT+=1
set EXE=%1 %= search output of tasklist for EXE =%
set tl=tasklist /NH /FI "IMAGENAME eq %EXE%"
FOR /F %%x IN ('%tl%') DO IF %%x == %EXE% goto FOUND
set result=0
goto FIN
:FOUND
set result=1
:FIN
IF %result% EQU 0 GOTO END
PING -n 2 127.0.0.1 > nul %= wait for about 1 second =%
GOTO BEGIN
:END
Ответ 11
запустить приложение, дождаться загрузки
@echo off & setlocal enabledelayedexpansion
start application.exe
:1
for %%a in (^| ^/ ^- ^\ ^| ^/ ^- ^\) do (
for %%b in (^| ^/ ^- ^\ ^| ^/ ^- ^\) do (
for %%c in (^| ^/ ^- ^\ ^| ^/ ^- ^\) do (
cls &echo processing..%%c%%b%%a
sleep -m 20
IF EXIST "result file" (exit)
)))
goto 1
Ответ 12
: LOOP ECHOX -n "~ r% Обработка..." IF% CTR% EQU 4 SET/A CTR = 0 IF% CTR% == 0 (set/p DOT = ³)