batch file - off - ¿Cómo codificar un spinner para procesos en espera en un archivo Batch?
echo off bat (12)
Esto realmente se puede hacer bastante fácilmente con comandos nativos puros, solo tienes que saber cómo usar los más difíciles. No es necesario el uso de herramientas externas como VBScript o efectos secundarios desagradables como borrar la pantalla.
Lo que estás buscando es el equivalente al comando bash " echo -n
" que genera una línea sin la nueva línea. En el lote de XP, esto se logra usando " set /p
" (pida al usuario que responda con un mensaje) con la entrada vacía de la siguiente manera:
<nul (set /p junk=Hello)
echo. again.
dará salida a la cadena "Hola otra vez". sin intercalar línea nueva.
Ese truco (y el uso de CTRL-H, el carácter de retroceso se puede ver en el siguiente script de prueba que comienza (uno después del otro) una subtarea de 10 segundos con un tiempo de espera de 20 segundos y un subtítulo de 15 segundos tarea con un tiempo de espera de 10 segundos.
La secuencia de comandos de carga útil es creada por la secuencia de comandos en ejecución real y su único requisito es que haga el trabajo que tiene que hacer y luego elimine un archivo de marca cuando finalice, para que la función de monitor pueda detectarlo.
Tenga en cuenta que las cadenas ^ H en este script son en realidad caracteres CTRL-H, el ^ | es dos caracteres separados utilizados para escapar del símbolo de la tubería.
@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
Me gustaría mostrar al usuario con un spinner, que algo se hace en segundo plano, pero no sé cómo funciona esto en un archivo por lotes.
¿Alguien tiene una pista?
Puede usar un contador que imprima un carácter diferente de un conjunto determinado (como "/ | / -") y cambie el carácter de acuerdo con "contador módulo 4". De todos modos, no dice en qué idioma está trabajando, por lo que es un poco difícil ser más preciso.
EDITAR: ahora que sabemos en qué entorno estás jugando, diría que el lenguaje BAT / CMD no está a la altura de la tarea ... Recomendaría cualquier lenguaje de scripting, siendo Ruby mi favorito.
Si entiendo tu pregunta, quieres un spinner porque alguna operación que estás realizando lleva tiempo y quieres mostrarle al usuario que algo está sucediendo, ¿no?
En ese caso, hasta donde yo sé, no es posible con los comandos nativos. (Podría ser posible si tuvieras un programa que mostrara un spinner mientras ejecutabas la operación que lleva mucho tiempo)
Y parece que el eco no es compatible con las secuencias de escape ansi (en los viejos tiempos tenías que tener ansi.sys cargada, no sé si aún existe) por lo que no puedes usar ansi para controlar el cursor.
Si no te importa que la pantalla se borre ... prueba esto:
@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
EDITAR: Esta muestra iniciará la Calculadora y luego mostrará un "control giratorio" hasta que cierre la Calculadora. Uso pslist para verificar la existencia de CALC.EXE. El > nul 2> & 1 redirige STDOUT y STDERR a nul, por lo que no se mostrará nada de PSLIST.
Si te refieres a un script por lotes de Windows, no puedes hacerlo de forma nativa. La instrucción de eco utilizada para imprimir en la consola siempre imprimirá una nueva línea y no podrá mover el cursor.
Es un truco, pero puedes hacerlo con una combinación de VBScript y script por lotes.
Este VBScript imprimirá un retroceso, luego es un argumento:
WScript.StdOut.Write(chr(8) & WScript.Arguments(0))
Coloque esto en un archivo, vbsEcho.vbs
, luego llame a este script desde su secuencia de comandos por lotes. El siguiente script por lotes seguirá mostrando el spinner hasta que presione CTRL-C:
@echo off
:LOOP
cscript //nologo vbsEcho.vbs "/"
cscript //nologo vbsEcho.vbs "|"
cscript //nologo vbsEcho.vbs "/"
cscript //nologo vbsEcho.vbs "-"
goto :LOOP
EDITAR: Usando algunas de las ideas de la respuesta de aphoria, este script iniciará la calculadora de Windows y mostrará un spinner hasta que la calculadora se cierre:
@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."
El spinner PUEDE hacerse en secuencia de comandos por lotes, solo necesita algunas variables:
@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 que el BAT mismo detecte un proceso que se ejecuta / sale. Puede hacerlo a través de la interfaz de la línea de comandos de WMI o del comando de la lista de tareas del cual tengo conocimiento limitado.
Si estuviese de vuelta en los días de DOS, incluso podría hacerlo sin borrar la pantalla ... salvo usar una combinación de caracteres de escape. No sé si todavía es posible en Vista / XP.
: LOOP ECHOX -n "~ r% Processing ..." IF% CTR% EQU 4 SET / A CTR = 0 IF% CTR% == 0 (set / p DOT = ³)
Encuentro que la manera más fácil es actualizar el título, de esa manera no tienes que hacer un CLS todo el tiempo.
El motivo de las dos líneas de ping -n es que es más rápido hacer un doble ping de un segundo cada una, en comparación con un único ping de dos segundos.
Además, para aquellos que no saben, a :: es lo mismo que un REM, excepto que los comentarios se ignoran al comienzo del analizador sintáctico (creo que esta es la palabra correcta) en lugar de al final. En pocas palabras, esa línea se ignora.
:: 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 tiene una respuesta increíble, pero tener que hacer eco de tus comandos en un archivo de carga útil es molesto. Es difícil de leer y difícil de depurar. Tomé su código y lo modifiqué un poco para mi propio 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
Prueba esto:
@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%
Esta rutina examina el resultado de la lista de tareas para un proceso que START desde cmd.
Pase el nombre del exe como parámetro, por ejemplo
llamada: spinner calc.exe
Informa
Transcurrido: 001 segundos
e incrementa segundos hasta que el proceso exe finaliza.
El mensaje transcurrido de 001 segundos se sobrescribe cada segundo por ECHO.exe -n / r
que echos un cr sin una alimentación de línea.
Echo.exe está disponible en 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
iniciar la aplicación, esperar a la carga
@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