How can I show the progress of a long running operation in windows batch (cmd) file in percentage? Can you share some example code?
Here is how...
Note: This code is a slightly modified version of this answer.
#echo off
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
FOR /L %%n in (1,1,10) DO (
call :show_progress %%n 10
ping localhost -n 2 > nul
)
echo Done!
exit /b
:show_progress
setlocal EnableDelayedExpansion
set current_step=%1
set total_steps=%2
set /a "progress=(current_step * 100) / total_steps"
set /p ".=Progress: !progress!%%!CR!" <nul
if !progress! equ 100 echo.
exit /b
An example would be scanning a large file/database, showing progress instead of a blinking cursor, for ages!
set c=<no. of lines in file>
for /l %i in (1,1,%c%) do cls & set /p="Scanning for target ... "<nul & (set /a per=%i00/%c% >nul & echo !per!%)& ping 127.0.0.1 -n 2 > nul & REM when target found, send agents to greet
echo Complete.
The code in brackets () are for progress percentage.
Tested in Win 10 CmD>
Related
Here is what I am trying to do. Suppose I have a program called myprogram.exe, which I have to execute 1000 times.
Under Windows, I could usually do something as simple as:
for /L %n in (1,1,1000) do start /myfolder/myprogram.exe
However, suppose I only have 5 CPU threads I can devote to running the 1000 instances of myprogram.exe, such that I launch only 5, then when one of these finishes another one is launched, etc until the whole 1000 end.
Under Linux and using GNU Parallel, I could simply do:
seq 1000 | parallel -N0 -j5 "nohup myprogram.exe"
How could I achieve something like that in Windows command line? Notice that in my case using Cygwin is not an option, so resorting to xargs and GNU Parallel under Windows are not options either.
Here is a way, by using powershell to do the process count. and using a simply set /a as counter.
#echo off
setlocal enabledelayedexpansion
set /a cnt=0
:counter
if !cnt! lss 1000 (
for /F "tokens=*" %%i in ('powershell ^(Get-Process -Name 'myprogram'^).count') do set proc=%%i
if !proc! lss 5 (
start "C:\myfolder\myprogram.exe"
set /a cnt+=1
)
goto :counter
)
You could add echo !cnt! in the line before goto :counter if you want to see it count.
It can be done without using delayedexpansion but I prefer to use it here.
This will run five processes in parallel. Each time one of them finishes, the next process will be started (so there are always 5 of them until they all are done)
#ECHO off
setlocal enabledelayedexpansion
set bunch=5
for /l %%a in (1,1,1000) do (
call :loop
echo processing: %%a
start "MyCommand" cmd /c timeout !random:~-1!
)
call :loop
goto :eof
:loop REM waits for available slot
for /f %%x in ('tasklist /fi "windowtitle eq MyCommand" ^| find /c "cmd.exe"') do set x=%%x
if %x% geq %bunch% goto :loop
goto :eof
Add the /min switch to start to minimize the started processes.
Give them a unique windowtitle (MyCommand here) to be able to count them.
Replace cmd /c timeout !random:~-1! with your actual command.
EDIT a slightly modified script, which may work better, if myprogram is a GUI (script above will work better with CLI applications):
#ECHO off
setlocal enabledelayedexpansion
set bunch=5
for /l %%a in (1,1,100) do (
call :loop
echo processing: %%a
start notepad.exe
)
call :loop
goto :eof
:loop REM waits for available slot
for /f %%x in ('tasklist /fi "imagename eq Notepad.exe"^|find /c "."') do set x=%%x
if %x% geq %bunch% goto :loop
goto :eof
I have tried to make Windows batch file that blinking the word "Wait" & "Wait..". I tried the following code:
#Echo OFF
setlocal EnableDelayedExpansion
for /f %%a in ('copy /Z "%~f0" nul') do set "CR=%%a"
SET p=-1
set num=2
set st[1]=Wait
set st[2]=Wait..
set st[3]=eer
:LOOP
if /i %num% equ 1 (
set num=2
) else (
set num=1
)
<nul set /P "=!st[%num%]!!CR!"
TIMEOUT /T 1 >NUL
GOTO :LOOP
The problem here, the IF seems to be worked only one time. i.e running the batch makes it prompt "Wait" only one time, then "Wait.." forever. What is the mistake here?
You problem is not the if (that works as intended), your problem are the spaces that should delete/overwrite the dots.
set "st[1]=Wait "
set "st[2]=Wait.."
I would like to write on the same line inside a loop in a windows batch file.
For example:
setlocal EnableDelayedExpansion
set file_number=0
for %%f in (*) do (
set /a file_number+=1
echo working on file number !file_number!
something.exe %%f
)
setlocal DisableDelayedExpansion
This will result in:
echo working on file number 1
echo working on file number 2
echo working on file number 3
.
.
.
I would like all of them to be on the same line.
I found a hack to remove the new line (e.g. here: Windows batch: echo without new line), but this will produce one long line.
Thanks!
#echo off
setlocal enableextensions enabledelayedexpansion
for /f %%a in ('copy "%~f0" nul /z') do set "CR=%%a"
set "count=0"
for %%a in (*) do (
set /a "count+=1"
<nul set /p ".=working on file !count! !CR!"
)
The first for command executes a copy operation that leaves a carriage return character inside the variable.
Now, in the file loop, each line is echoed using a <nul set /p that will output the prompt string without a line feed and without waiting for the input (we are reading from nul). But inside the data echoed, we include the carriage return previously obtained.
BUT for it to work, the CR variable needs to be echoed with delayed expansion. Otherwise it will not work.
If for some reason you need to disable delayed expansion, this can be done without the CR variable using the for command replaceable parameter
#echo off
setlocal enableextensions disabledelayedexpansion
for /f %%a in ('copy "%~f0" nul /z') do (
for /l %%b in (0 1 1000) do (
<nul set /p ".=This is the line %%b%%a"
)
)
Thanks to the answer of MC ND I have a created a subroutine, echocr, that you can call without
delayed expansion, that will echo a string with only a carriage return,
and no newline. (The spaces after %input% are adjusted to cover all previous messages).
You can use it to overwrite a line as shown in the modified
code:
#echo off
call :echocr "good morning"
PING -n 2 127.0.0.1>nul
call :echocr "good afternoon"
PING -n 2 127.0.0.1>nul
call :echocr "bye now"
PING -n 2 127.0.0.1>nul
pause
:echocr
:: (echo string with carriage return, no line feed)
for /F "tokens=1 delims=# " %%a in (
'"prompt #$H# & echo on & for %%b in (1) do rem"'
) do set "backspace=%%a"
set input=%~1
set "spaces40= "
set "spaces120=%spaces40%%spaces40%%spaces40%
for /f %%a in ('copy "%~f0" nul /z') do (
set /p ".=*%backspace%%spaces120%%%a" <nul
set /p ".=*%backspace%%input%%%a" <nul
)
exit /b
The above no longer works in Windows 7 and later.
I found the reason is delayed expansion that should be set after ASCII_13 assignment, maybe someone smart could explain why exactly.
Anyway, the code below works both on Windows 7 and Windows 10.
#set licz=0
#setlocal
#for /f %%a in ('copy /Z "%~dpf0" nul') do #set "ASCII_13=%%a"
#setlocal enabledelayedexpansion
:loop
#set /a licz=licz+1
#set /p "=Waiting time: %licz% seconds!ASCII_13!" <NUL
#Timeout /T 1 /Nobreak > NUL
#GOTO loop
If you do not like the cursor blinking at beginning of the line, transfer ASCII_13 to the beginning to execute CR before text.
Needs to be preceeded by any ASCII character, though, to avoid getting stripped. And this will be visible as the last char on the line, so be wary here :)
#set /p "=.!ASCII_13!Waiting time: %licz% seconds" <NUL
#echo off
setlocal enableextensions enabledelayedexpansion
Rem Get a carriage return character
set "CR=" & for /f %%a in ('copy /Z "%~f0" nul') do if not defined CR set "CR=%%a"
rem The progress bar
set "fill=[###################]"
echo(
rem For each character in the fill
for /l %%a in (2 3 21) do (
rem Calculate the right part of the bar
set "spaces=!fill:~%%a!"
rem Output the left and right parts of the bar and carriage return
<nul set/p ".=:: Please Connect Device : !fill:~0,%%a!!spaces:#= !!CR!"
rem Pause for a second
ping -n 2 "" > nul
)
echo(
I have a batch file that will display all files in a folder and allow the user to select a file by number and pass it to an executable. Everything works, however, I am trying to make this a click-and-forget type of batch file. In other words, I would also like the user to be able to select a number that would pass all files, one at a time, to said executable. My current code is as follows:
#ECHO OFF
SET index=1
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%f IN (*.txt) DO (
SET file!index!=%%f
ECHO !index! - %%f
SET /A index=!index!+1
)
SETLOCAL DISABLEDELAYEDEXPANSION
SET /P selection="select file by number:"
SET file%selection% >nul 2>&1
IF ERRORLEVEL 1 (
ECHO invalid number selected
EXIT /B 1
)
CALL :RESOLVE %%file%selection%%%
CSCRIPT //NoLogo //B C:\FCLoader\sendkeys.vbs
Z:\executable.exe %file_name%
GOTO :EOF
:RESOLVE
SET file_name=%1
GOTO :EOF
The CSCRIPT line is there to pass keystrokes from a vbs file to enter username, password, and a database name to the executable. This works fine. The executable works fine too if I just want to run it once against one file. Below is an example of what the batch looks like after running:
1 - file.txt
2 - file2.txt
3 - file3.txt
select file by number:
What I would like is for it to look like this:
1 - file.txt
2 - file2.txt
3 - file3.txt
4 - All
select file by number:
I would like for the user to be able to select 4 - All, and have it run the executable for every file, one at a time. When the first run through completes, it will call the executable again and so on...It will also need to run the vbs script before the executable is run in order for the credentials to be passed as stated above.
I have used the extent of my knowledge in Batch Files and now need help.
Try this code:
#ECHO OFF
SET index=1
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%f IN (*.*) DO (
SET file!index!=%%f
ECHO !index! - %%f
SET /A index=!index!+1
)
echo/!Index! - All
SETLOCAL DISABLEDELAYEDEXPANSION
SET /P selection="select file by number: "
if "%selection%" equ "%index%" goto allFiles
SET file%selection% >nul 2>&1
IF ERRORLEVEL 1 (
ECHO invalid number selected
EXIT /B 1
)
CALL :RESOLVE %%file%selection%%%
CSCRIPT //NoLogo //B C:\FCLoader\sendkeys.vbs
Z:\executable.exe "%file_name%"
GOTO :EOF
:RESOLVE
SET file_name=%1
GOTO :EOF
:allFiles
FOR %%f IN (*.txt) DO (
CSCRIPT //NoLogo //B C:\FCLoader\sendkeys.vbs
Z:\executable.exe "%%f"
)
goto :EOF
Pick a file from a numbered list 1 to [a lot]!
This uses the choice command if less than 10 files (max files > 9999?? untested)
Files are numbered starting at 1 (not 0) - so 1-9.
Choice has timeout and default - but if more than 9 files no timeout
If more than 9 files and user presses ENTER key it uses the default value
Files are listed in date/time order - most recent first
A path and wildcard filter (* or ?) can be set for the dir command.
It could be adapted to run ALL as requested?
#echo off
SETLOCAL ENABLEEXTENSIONS
SETLOCAL ENABLEDELAYEDEXPANSION
mode con: cols=185 lines=90
COLOR 1F
set MYPATH="G:\Downloads\*.iso"
set DEFAULT=1
set TIMEOUT=3
:GETISO
echo.
echo ISO FILES IN %MYPATH%
echo.
set count=1
set choose=
for /f "tokens=*" %%i in ('dir %MYPATH% /b/a-d/od/t:c') do (echo !count! %%i & set choose=!choose!!count!& set /A count=!count!+1)
echo.
set /a count=%count%-1
if %count%==0 echo NO FILES FOUND! & goto :EOF
if %count% LSS 10 CHOICE /C "%choose%" /N /M "SELECT DESIRED ISO (1-%count%) : " /T %TIMEOUT% /D %DEFAULT%
if %count% LSS 10 set /A ask=%errorlevel%+0
if %count% GEQ 10 (
set ask=%DEFAULT%
set /P ask="SELECT DESIRED ISO (1-%count%) : "
set /A ask=%ask+0
)
if "%ask%"=="" echo INPUT ERROR! & goto :GETISO
if "%ask%"=="0" echo INPUT ERROR! & goto :GETISO
if %ask% GTR %count% echo ERROR - NUMBER TO BIG! & goto :GETISO
set count=1
for /f "tokens=*" %%i in ('dir %MYPATH% /b/a-d/od/t:c') do (
if !count!==%ask% set FILE=%%i
set /A count=!count!+1
)
echo.
echo FILE=%ask% %FILE%
So far I've been trying to make a continuous .bat file that will start the server file, read every line that comes down and if the response "Server has become unresponsive" then the bat will close the file and re-open(this needs to be done every hour or so and I'm not always at the computer)
I do believe this is the correct code but I need to double check with some tech-savy minds to see if it's correct.
#echo off
SETLOCAL DisableDelayedExpansion
FOR /F "usebackq delims=" %%A in (`"findstr rust_server/n ^^ "`) do (
set "myVar=%%A"
call :processLine myVar
)
goto :eof
:processLine
SETLOCAL EnableDelayedExpansion
set "line=!%1!"
set "line=!line:*:=!"
echo(!line!
Find /I /V "Unresponsive for 10"
taskkill /fi "WindowTitle eq rust_server*"
start /d "C:\Rust Server" rust_server.exe
ENDLOCAL
goto :eof
Any ideas/suggestions would be greatly appreciated.
#Echo off
Title SERVER RESTARTER (place with your rust_server exe)
color 1f
SET n=0
:Loop
SET /A n=n+1
echo Server Restarter
echo -----Restart Server Batch VERSION-----
taskkill /IM rust_server.exe
echo Opening rust_server.exe server again
rust_server.exe
if %n% EQU 60 (
exit
) Else if %n% LEQ 24 (
Goto Loop