10

I want to use a real time progress bar where it updates as code is written or installing something or loading a file.

Example:

@echo off

:main
echo Updating file...
[PROGRESS BAR HERE]

The "PROGRESS BAR HERE" indicates the part that I want the real time progress bar to be placed at.

MegaRodeon
  • 315
  • 2
  • 3
  • 10

5 Answers5

9

Just a skelleton. Adapt as needed.

The basic idea is to output the line with the progress bar with an ending carriage return to return to the start of the line and be able to repaint the next state over the previous one.

All "problematic" code wrapped into subroutines so you only need to call :drawProgressBar percentValue "operationText"

@echo off

    setlocal enableextensions disabledelayedexpansion

    for /l %%f in (0 1 100) do (
        call :drawProgressBar %%f "up test with a long text that will not fit on screen unless you have a lot of space"
    )
    for /l %%f in (100 -1 0) do (
        call :drawProgressBar %%f "going down test"
    )
    for /l %%f in (0 5 100) do (
        call :drawProgressBar !random! "random test"
    )

    rem Clean all after use
    call :finalizeProgressBar 1


    call :initProgressBar "|" " "
    call :drawProgressBar 0 "this is a custom progress bar"
    for /l %%f in (0 1 100) do (
        call :drawProgressBar %%f 
    )

    endlocal
    exit /b


:drawProgressBar value [text]
    if "%~1"=="" goto :eof
    if not defined pb.barArea call :initProgressBar
    setlocal enableextensions enabledelayedexpansion
    set /a "pb.value=%~1 %% 101", "pb.filled=pb.value*pb.barArea/100", "pb.dotted=pb.barArea-pb.filled", "pb.pct=1000+pb.value"
    set "pb.pct=%pb.pct:~-3%"
    if "%~2"=="" ( set "pb.text=" ) else ( 
        set "pb.text=%~2%pb.back%" 
        set "pb.text=!pb.text:~0,%pb.textArea%!"
    )
    <nul set /p "pb.prompt=[!pb.fill:~0,%pb.filled%!!pb.dots:~0,%pb.dotted%!][ %pb.pct% ] %pb.text%!pb.cr!"
    endlocal
    goto :eof

:initProgressBar [fillChar] [dotChar]
    if defined pb.cr call :finalizeProgressBar
    for /f %%a in ('copy "%~f0" nul /z') do set "pb.cr=%%a"
    if "%~1"=="" ( set "pb.fillChar=#" ) else ( set "pb.fillChar=%~1" )
    if "%~2"=="" ( set "pb.dotChar=." ) else ( set "pb.dotChar=%~2" )
    set "pb.console.columns="
    for /f "tokens=2 skip=4" %%f in ('mode con') do if not defined pb.console.columns set "pb.console.columns=%%f"
    set /a "pb.barArea=pb.console.columns/2-2", "pb.textArea=pb.barArea-9"
    set "pb.fill="
    setlocal enableextensions enabledelayedexpansion
    for /l %%p in (1 1 %pb.barArea%) do set "pb.fill=!pb.fill!%pb.fillChar%"
    set "pb.fill=!pb.fill:~0,%pb.barArea%!"
    set "pb.dots=!pb.fill:%pb.fillChar%=%pb.dotChar%!"
    set "pb.back=!pb.fill:~0,%pb.textArea%!
    set "pb.back=!pb.back:%pb.fillChar%= !"
    endlocal & set "pb.fill=%pb.fill%" & set "pb.dots=%pb.dots%" & set "pb.back=%pb.back%"
    goto :eof

:finalizeProgressBar [erase]
    if defined pb.cr (
        if not "%~1"=="" (
            setlocal enabledelayedexpansion
            set "pb.back="
            for /l %%p in (1 1 %pb.console.columns%) do set "pb.back=!pb.back! "
            <nul set /p "pb.prompt=!pb.cr!!pb.back:~1!!pb.cr!"
            endlocal
        )
    )
    for /f "tokens=1 delims==" %%v in ('set pb.') do set "%%v="
    goto :eof
MC ND
  • 69,615
  • 8
  • 84
  • 126
  • All this is required just for a progress bar? Is there no one-liner? – Slava Apr 30 '18 at 15:13
  • 1
    @Alph.Dev, I have updated the code with a slightly shorter version I posted on a newer answer. The code does a lot of things: determine screen width, create padding buffer, retrieve needed special characters, but once included in a batch file, you almost only need the `call :drawProgressBar`. But you don't need all this code, just take the part you need. The basic idea is just to keep overwritting the same line in screen. – MC ND Apr 30 '18 at 20:14
  • +1, I never knew you could actually parse parameters on a label using call. Wow, you learn something new everyday! – LPChip Jul 19 '21 at 19:18
  • 1
    @LPChip: You don't parse parameters on a label, but in a _subroutine_. There are two ways to define a subroutine: an _external_ one defined in a separate .bat file and called via `call otherfile.bat`, and an _internal_ one defined after a :label in the same .bat file and called via `call :label`. Both types of subroutines works in the exact same way with the sole exception that any file-related modifier applied to parameter `%0` (like `%~F0` or `%~N0`) gives the value of the .bat file (not of the label); this behavior is rather logical... – Aacini Jul 29 '21 at 20:41
6

It is much simpler to show the progress bar in the Window title:

@echo off
setlocal EnableDelayedExpansion

rem Count the number of files in this dir (just as an example)
set n=0
for %%f in (*.*) do set /A n+=1

rem Fill "bar" variable with 70 characters
set "bar="
for /L %%i in (1,1,70) do set "bar=!bar!X"

rem Fill "space" variable with filler spaces
set "space="
for /L %%i in (1,1,110) do set "space=!space!_"

rem "Process" the files and show the progress bar in the title
set i=0
echo Processing files:
for %%f in (*.*) do (
   set /A i+=1, percent=i*100/n, barLen=70*percent/100
   for %%a in (!barLen!) do title !percent!%%  !bar:~0,%%a!%space%
   echo !i!- %%f
   ping -n 1 localhost > NUL
)

title MS-DOS

Perhaps you have to adjust the lenght of bar and space variables a little...

Aacini
  • 65,180
  • 12
  • 72
  • 108
2
@echo off
::PROGRESS BAR
set /a full = 50
for /l %%a in (1,1,%full%) do (
 CALL:ADDSPACE)
for /l %%b in (1,1,%full%) do (
 CALL:PROGRESS %%b
)
del _temp.bat
exit/b
:ADDSPACE
 set "fullBar=%fullBar%_"
 set "tags=%tags%#"
 exit/b
:PROGRESS
set number=%~1
set /a pct=full-number
 (
  echo/echo/[%%tags:~0,%~1%%%%fullBar:~0,%pct%%%]
 )>_temp.bat
 call _temp.bat
 timeout 1 1>nul&cls
Rafael
  • 3,042
  • 3
  • 20
  • 36
2

Ok the use of a progress bar should be along side a running install. I have got round this querying the tasklist in my script. So for example i have put notepad.exe, thus the progress bar will run until notepad has been closed, replace the vairable myapp=setup.exe and it will do the same with another application. I have made my script change colour as the progress bar moves also.

@echo off
set load=
set loadnum=0
set flash=0

set installspeed=2
set myapp=notepad.exe

:progressinstall
set load=%load%Û
cls
echo.
echo.
echo.
echo  INSTALLING PLEASE WAIT
echo ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
echo  %load%
echo ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
ping localhost -n %installspeed% >nul
set/a loadnum=%loadnum% +1
set/a flash=%flash% +1
if %flash% == 9 set flash=0
color 0%flash%
if %loadnum% == 24 set/a loadnum=0 & set load=
tasklist | find "%myapp%" > NUL
If errorlevel 1 goto installcomplete
goto progressinstall

:installcomplete
color 07
cls
echo.
echo.
echo.
echo  INSTALLATION COMPLETE
echo ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
echo  ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
echo ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
echo.
pause
cls
exit
Rich
  • 21
  • 1
0

Small modification from Aacini that is working nicer. The bar is almost not growing and you can choose the length of the bar.

enter image description here

@echo off
setlocal EnableDelayedExpansion

rem Count the number of files in this dir (just as an example)
set n=0
for %%f in (*.*) do set /A n+=1

set /a lengthbar=100
rem Fill "bar" variable with 70 characters
set "bar="
for /L %%i in (1,1,%lengthbar%) do set "bar=!bar!X"

rem Fill "space" variable with filler spaces
set "space="
for /L %%i in (1,1,%lengthbar%) do set "space=!space!_"

rem "Process" the files and show the progress bar in the title
set i=0
echo Processing files:
for %%f in (*.*) do (
    set /A i+=1, percent=!i!*%lengthbar%/!n!
    for %%a in (!percent!) do (
        set bar2=!bar:~0,%%a!
        set /a left=%lengthbar%-%%a
        for %%b in (!left!) do (
            set space2=!space:~-%%b!
            title %%a%% !bar2!!space2!
        )
    )
    echo !i!- %%f
    ping -n 5 localhost > NUL
)

EDIT: for learning purpose.

The code of Aacini: enter image description here

My code:

enter image description here

Dorian Grv
  • 421
  • 5
  • 9
  • Some notes about your code: **1-** You may initialize both `bar` and `space` variables in _the same_ `for /L %%i in (1,1,%lengthbar%) do` command. **2-** You don't need to !expand! variables in SET /A command, so `set /A i+=1, percent=i*lengthbar/n` gives the same result... Well, _almost_, because the `!i!` expansion gives the value of _the previous_ iteration, NOT of the current `i+=1` one. **3-** (Conceptual error): Your `percent` variable does _not_ represent a percentage, it represents _the length_ of the "X" (left side) bar, so the `title %%a%% ...` part is wrong. – Aacini Jul 06 '22 at 06:27
  • **4-** (Conceptual error): Your `left` variable represents the length of the _right side_ (spaces) bar. **5-** If you change the line before the FOR for this: `set /A i+=1, percent=i*100/n, barLen=lengthBar*percent/100` (just change the original `70` by `lengthBar`) and recover the original `for %%a in (!barLen!) do` command, then this sole command: `title !percent!%% !bar:~0,%%a!!space:~%%a!` shows the same output than your 6 lines modification! Your code is convoluted and inefficient... – Aacini Jul 06 '22 at 06:27
  • Hi Thanks for all your comments. I modified the code without big background of cmd. The bar was growing while the percentage was increasing and I tried the way I could. **1-** I agree. **2-** I did know this trick. **3-** Yes I guess that it is just the number of characters X. **4-** Same. **5-** Mmmm I guess you are right but the output seems different and the bar is almost not growing with my code. I add 2 gifs in my post and let the code as it is. I guess all your comment (almost) will bring my code back to yours :) – Dorian Grv Jul 07 '22 at 08:57