Como codificar um botão rotativo para processos de espera em um arquivo de lote?

StackOverflow https://stackoverflow.com/questions/368041

  •  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?

Foi útil?

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 = ³)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top