Como codificar um botão rotativo para processos de espera em um arquivo de lote?
-
21-08-2019 - |
Pergunta
Eu gostaria de mostrar ao usuário um girador, que algo é feito no fundo, mas não sei como isso funciona em um batchfile.
Alguém tem uma pista?
Solução
Isso pode realmente ser feito muito facilmente com comandos nativos puros, você apenas tem que saber como usar o mais complicado deles. Sem uso de ferramentas externas, como VBScript ou efeitos colaterais desagradáveis ??como limpar a tela são necessárias.
O que você está procurando é o equivalente do comando bash "echo -n
", que envia uma linha sem a nova linha. No lote XP, isso é conseguido através de "set /p
" (pergunte ao usuário para a resposta com um prompt) com entrada vazia da seguinte forma:
<nul (set /p junk=Hello)
echo. again.
saída será a string "Olá de novo." sem nova linha de intervenção.
Esse truque (e o uso de CTRL-H, o personagem de retrocesso pode ser visto no script de teste seguinte, que se inicia (uma após a outra) a 10 segundos sub-tarefa com um 20-segundo tempo e um 15- segunda sub-tarefa com a 10 segundo tempo.
O script de carga é criado pelo script de funcionamento real e sua única exigência é que fazer o trabalho que tem que fazer, em seguida, excluir um arquivo bandeira quando terminar, para que a função de monitor será capaz de detectá-lo.
Tenha em mente que os ^ cordas H neste roteiro são realmente caracteres CTRL-H, o ^ | é dois caracteres separadas usadas para escapar do símbolo pipe.
@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
Outras dicas
Tente isto:
@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%
Se você não se importa a clareira tela ... tente o seguinte:
@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: Este exemplo irá iniciar a Calculadora e, em seguida, exibir um "Spinner" até fechar Calculator. Eu uso pslist para verificar a existência de CALC.EXE. A > nul 2> & 1 redireciona STDOUT e STDERR para nul por isso nada de pslist será exibida.
Se entendi sua pergunta você quer um spinner porque alguma operação que você está executando é a tomada de tempo e você quer mostrar para o usuário que algo está acontecendo, certo?
Nesse caso, tanto quanto eu sei, não é possível com os comandos nativos. (Que poderia ser possível se você tivesse um programa que mostrou um spinner ao executar a operação que demorou muito tempo)
E parece que o eco não suportam ANSI seqüências de escape (nos velhos tempos você tinha que ter ansi.sys carregado, não sei se isso ainda existe) para que você não pode usar ansi para controlar o cursor .
O spinner CAN ser feito no script em lotes, você só precisa de algumas variáveis:
@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
Para o próprio BAT para detectar um processo em execução / saídas. Você pode fazer isso através do WMI de linha de comando da interface ou o comando tasklist de que tenho conhecimento limitado.
Se fosse nos dias DOS você poderia até mesmo faz isso sem limpar a tela ... curto de usar uma combinação de caracteres de escape. Eu não sei se ele ainda está disponível no Vista / XP.
Se você quer dizer dentro de um script de lote do Windows, você não pode fazê-lo de forma nativa. A declaração echo usado para imprimir para o console será sempre imprimir uma nova linha, e você não pode mover o cursor.
É um pouco de um truque, mas você pode fazer isso com uma combinação de VBScript e script em lotes.
Esta VBScript irá imprimir um retrocesso, então é argumento:
WScript.StdOut.Write(chr(8) & WScript.Arguments(0))
Coloque isso em um arquivo, vbsEcho.vbs
, em seguida, chamar esse script de seu script em lotes. O script a seguir lote manterá exibindo o botão rotativo até que você pressione CTRL-C:
@echo off
:LOOP
cscript //nologo vbsEcho.vbs "\"
cscript //nologo vbsEcho.vbs "|"
cscript //nologo vbsEcho.vbs "/"
cscript //nologo vbsEcho.vbs "-"
goto :LOOP
EDIT: Usando algumas das idéias de resposta das aphoria, este script irá iniciar a calculadora do Windows, e exibir um spinner até o fechamento da calculadora:
@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."
Você pode usar um contador que imprime um caráter diferente a partir de um determinado conjunto (como "\ | / -") e você mudar o caráter de acordo com como "contador de módulo 4". De qualquer forma, você não diz em que língua você está trabalhando por isso é um pouco difícil para ser mais preciso.
EDIT: agora que sabemos em qual ambiente você está jogando em, eu diria que o / idioma CMD BAT não é realmente à altura da tarefa ... Eu recomendo qualquer linguagem de script, Ruby sendo o meu favorito .
Acho que a maneira mais fácil é para atualizar o título -. Dessa forma, você não tem que fazer uma CLS o tempo todo
A razão para as duas linhas de pingue -n, é que é mais rápido para ping para fazer uma dupla de ping de um segundo cada, contra um único ping dois segundos.
Além disso, para aqueles que não sabem, a :: é o mesmo que a REM, exceto que os comentários são ignorados no início do analisador (acho que esta é a palavra certa) em vez de no final. Simplificando, essa linha é ignorado.
:: 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
paxdiablos tem uma resposta surpreendente, mas ter a ecoar os seus comandos em um arquivo de carga útil é irritante. É difícil de ler e difícil de depuração. Peguei seu código e modifiquei um pouco para meu próprio uso:
@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
Esta rotina examina a saída da lista de tarefas para um processo de você começar a partir cmd.
Passe-o nome do exe como um parâmetro por exemplo,
ligue para: girador calc.exe
ele relata
Decorrido: 001 segundos
e incrementos de segundos até que os termina processo exe.
A mensagem decorrido 001 segundos é substituído por cada segundo ECHO.exe -n \ r
ecos que uma cr sem uma linha de alimentação.
Echo.exe está disponível em
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
começar a aplicação, espera para carregamento
@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
: LOOP ECHOX -n "~ r% Processing ..." SE% CTR% EQU 4 SET / Um CTR = 0 SE% CTR% == 0 (set / p DOT = ³)