i have a text file logging timestamps from ffprobe on video durations of some video files, which that text file looks like this:
14.068700
5.043011
84.071967
5.043011
104.058600
5.043011
134.055234
5.056000 ....
I am trying to add these up, since batch files do not allow for floating numbers i chose to use powershell. here is my following code:
set total=0
for /f "tokens=*" %%x in (timestamps.txt) do (
set item=%%x
echo !item!
for /f "delims=." %%i in ('powershell %total% + %item%') DO SET total=%%i
echo %total%
)
but again it seems cause it is a floating number i am unable to do something like
SET /a total=%total% + %total%
so that i can not add that as a variable in this line:
powershell %total% + %item%
I have tried every combo i can think of with no luck, lots of searches and nothing comes back.
any idea how to do this or is there a better way to add up all these in pure batch ?
a pure Powershell solution would have been much simpler, but seeing as you require batch-file with powershell:
#echo off
setlocal enabledelayedexpansion & set nums=
for /f "usebackq delims=" %%x in ("timestamps.txt") do set nums=!nums!%%x+
for /f "delims=" %%i in ('powershell %nums:~0,-1%') do set "total=%%i"
echo Total: %total%
We just append all the numbers with the operator + to a variable, then pass that variable to powershell and get the result.
Note! delayedexpansion is needed because we are setting
or by utilizing powershell to do all the work and simply assign the result to the variable:
#echo off
for /f "delims=" %%i in ('"powershell -command (Get-Content -Path "timestamps.txt" ^| Measure-Object -Sum^).Sum"') do set "total=%%i"
echo Total: %total%
so this is what i came up with, not as elegant as it possibly can be BUT it is working. Wish it was a bit quicker but PowerShell slows things down a bit but definitely worth it!
its crazy to think when doing dos they never thought of using floating numbers, lol
anyways here is code:
set seconds=0
set item=0
for /f "tokens=*" %%t in (%filename%_copy_two%fileextension%) do (
set item=%%t
for /f %%i in ('powershell !item!+!seconds!') do (set seconds=%%i)
)
for /f %%i in ('powershell -NoP "[Math]::Round(!seconds!/ 60,2)"') do (set minutes=%%i)
set HH=00
for /f "tokens=1 delims=." %%r in ('echo %minutes%') do set min=%%r
for /f "tokens=2 delims=." %%p in ('echo %minutes%') do set sec=%%p
IF %sec% GTR 60 (
set /a newSec=!sec!-60
if !newSec! lss 10 set newSec=0!newSec!
set /a newMin=1 + !min!
if !newMin! lss 10 set newMin=0!newMin!
) else (
set /a newMin=!min!
set /a newSec=!sec!
)
IF %newMin% LSS 60 (
if !newMin! lss 10 set newMin=0!newMin!
set MM=!newMin!
if !newSec! lss 10 set newSec=0!newSec!
set SS=!newSec!
GOTO playlistTotal
)
IF %newMin% GEQ 60 IF %newMin% LSS 120 (
set /a MM=!newMin!-60
if !MM! lss 10 set MM=0!MM!
set /a HH=01
set SS=!newSec!
GOTO playlistTotal
)
IF %newMin% GEQ 120 IF %newMin% LSS 180 (
set /a MM=!newMin!-120
if !MM! lss 10 set MM=0!MM!
set /a HH=02
set SS=!newSec!
GOTO playlistTotal
)
IF %newMin% GEQ 120 IF %newMin% LSS 240 (
set /a MM=!newMin!-180
if !MM! lss 10 set MM=0!MM!
set /a HH=03
set SS=!newSec!
GOTO playlistTotal
)
IF !newMin! EQU 240 (
set /a MM=!newMin!-240
if !MM! lss 10 set MM=0!MM!
set /a HH=04
set SS=!newSec!
GOTO playlistTotal
)
IF %newMin% Gtr 240 (
ECHO We do not suggest a single playlist over 4 hours.
echo Please Go back edit your list to be shorter.
ECHO And just append to it
del %filename%_copy%fileextension%
del %filename%_copy_two%fileextension%
GOTO editPlaylist
)
:playlistTotal
del %filename%_copy%fileextension%
del %filename%_copy_two%fileextension%
echo.
Echo Playlist a total duration of = !HH!:!MM!:!SS!
echo.
IF !newMin! EQU 240 (
set /a MM=!newMin!-240
if !MM! lss 10 set MM=0!MM!
set /a HH=04
set SS=!newSec!
GOTO playlistTotal
)
IF %newMin% Gtr 240 (
ECHO We do not suggest a single playlist over 4 hours.
echo Please Go back edit your list to be shorter.
ECHO And just append to it
del %filename%_copy%fileextension%
del %filename%_copy_two%fileextension%
GOTO editPlaylist
)
:playlistTotal
del %filename%_copy%fileextension%
del %filename%_copy_two%fileextension%
echo.
Echo Playlist has a total duration of= !HH!:!MM!:!SS!
echo.
I hope this helps someone out! kind of stinks i cant ask a question for 6 months for this question considering there is NOT solution that I can find that met me OP
And once again I posted my solution after a question which I try to do every time, have a great day!
EDIT: thanks to #Gerhard
I have changed to this, but leaving the code here in case someone needs for a similar issue in a different way.
for /f "delims=" %%i in ('"powershell -command (Get-Content -Path "%filename%_copy_two%fileextension%" ^| Measure-Object -Sum^).Sum"') do set "seconds=%%i"
for /f %%i in ('powershell -NoP "[Math]::Round(!seconds!/ 60,2)"') do (set minutes=%%i)
Then keeping the same as is till someone posts a better solution.
You can achieve this in a very simple and pure Batch file that run much faster than any PS:
#echo off
setlocal
set /A factor=1000000, total=0
for /F "tokens=1,2 delims=." %%a in (timestamps.txt) do set /A total+=%%a*factor+1%%b-factor
echo %total:~0,-6%.%total:~-6%
Of course, this solution is so simple because it assumes that all input numbers have 6 decimals! If this is not the case, then the code should be modified accordingly...
I have a batch file which scans the computer taking various readings and storing each in to a variable. During this time I would like a little more than a simple message telling the user to 'Please wait - Gathering system information'.
I've tried the quick, easy, (and poor), way of doing this, as follows, as I want this to be a true incremental progress bar.
cls
echo.
echo.
echo Please wait - Gathering system information
echo ----------------------------------
echo Progress: ░░░░░░░░░░░░░░░░░░░░ 0%%
echo ----------------------------------
>nul timeout /t 1 &cls
cls
REM Do something here.
echo.
echo.
echo Please wait - Gathering system information...
echo ----------------------------------
echo Progress: █░░░░░░░░░░░░░░░░░░░ 5%%
echo ----------------------------------
>nul timeout /t 1 &cls
cls
REM Do something else here.
echo.
echo.
echo Please wait - Gathering system information.
echo ----------------------------------
echo Progress: ██░░░░░░░░░░░░░░░░░░ 10%%
echo ----------------------------------
>nul timeout /t 1 &cls
cls
REM And something else..
echo.
echo.
echo Please wait - Gathering system information..
echo ----------------------------------
echo Progress: ███░░░░░░░░░░░░░░░░░ 15%%
echo ----------------------------------
>nul timeout /t 1 &cls
cls
REM Etc...
However, the effect is not exactly great, as the cls command causes the command window to flicker, and there has to be a 'false' pause after each update, which slows the whole process… Also not great.
I have the following that looks great, but I have no idea how to 'increment' the bar after every section of code, as per my initial example, and having it play from 0-100% before running anything else is a bit pointless.
#echo off &cls
mode con: cols=70 lines=5 &color f0
call :setESC
chcp 65001 >nul
set progress=
set/a progressnum=0
:gettingdata
set progress=%progress%%ESC%[96m█%ESC%[30m
cls
echo.
echo. Please wait - Gathering system information...
echo. %progress% (%progressnum%/20)
ping localhost -n 2 >nul
set/a progressnum=%progressnum% +1
if %progressnum%==20 goto finished
goto gettingdata
:finished
echo.
echo. Finished
echo. %ESC%[92m████████████████████%ESC%[30m (20/20)
echo. Press any key to exit &>nul timeout /t -1 &exit /B
:setESC
REM Define escape char
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
set ESC=%%b
exit /B 0
)
So, my question is, how can I get the effect of the second script, but actually increment the bar after each section of my batch script has executed?
Ok I figured it out (with the help of this post).
Below is how I set up and used the script with only small additions to make it work with Unicode symbols.
First, set up your environment however you please. For the purpose of this post we will use the following:
#echo off &cls
mode con: cols=70 lines=4 &color f0
setlocal
Now in between each segment of your code you'll need to define the percentage of the progress bar by calling :drawProgressBar like so:
REM Your code here (segment 1)
REM - Progress Bar 0% - 33% ------------------------------------------------------
echo. Please wait - Gathering system information...
for /l %%f in (0 1 33) do (
call :drawProgressBar %%f "Code segment 1 finished"
)
REM ------------------------------------------------------------------------------
Repeat as required increasing the percentage each time like so:
REM Your code here (segment 2)
REM - Progress Bar 34% - 66% -----------------------------------------------------
for /l %%f in (34 1 66) do (
call :drawProgressBar %%f "Code segment 2 finished"
)
REM ------------------------------------------------------------------------------
REM Your code here (segment 3)
REM - Progress Bar 67% - 100% ----------------------------------------------------
for /l %%f in (67 1 100) do (
call :drawProgressBar %%f "Code segment 2 finished"
)
REM ------------------------------------------------------------------------------
REM The rest of your script goes here
endlocal
echo. &echo. All done. Press any key to Exit. &>nul timeout /t -1 &exit /B
Addendum: If you would like each segment to show 0-100% (as opposed to counting up from 0-100% overall) then you can use the following each time and simply update the description:
REM - Progress Bar 0% - 100% -----------------------------------------------------
for /l %%f in (0 1 100) do (
call :drawProgressBar %%f "Code segment finished"
)
REM ------------------------------------------------------------------------------
Ok to continue and this is personal preference here, but I then like to resize the CMD Window with another cls &mode con: cols=70 lines=60 &color f0 (or whatever size and/or colour you need) then continue to the output. As I say, this is just personal choice but I think it looks better with the progress meter in a smaller windows while the information is gathered before moving on to the 'main output' of the script.
Finally, add the following to the end of your batch file:
: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]
chcp 65001 >nul
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%"
chcp 1252 >nul
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
This is probably an easy task for the seasoned guys & gals here but I struggled with how to do this so I thought that I would share what worked for me and how I used the script.
Full credit goes to MC ND who is the original author of the script used. Please see the original post for more customisation options.
I have text documents filled with URLs and when I count the number of lines inside the file, as soon as the line count is greater than 1000, I want to delete the lines which follow those, keeping the first 1000 lines.
This is my code but I can't figure out what I am doing wrong.
Create a text.txt file with this script location and put over 1000 lines of junk into it to test.
#ECHO OFF & setLocal EnableDelayedExpansion
color 0A
%*
SET root_path=%~dp0
set text_name=text.txt
set text_name_output=output_text.txt
set max_line_count=1000
set /a counter=0
for /f %%a in (%root_path%%text_name%) do (
set /a counter += 1
echo Number of lines: !counter!
if !counter! LSS %max_line_count% (
echo less than
) else (
echo more than so delete these line numbers
(for /f "tokens=1,* delims=[]" %%a in ('type %root_path%%text_name%^|find /v /n ""') do (
echo/%%a|findstr /x "!counter!" >nul || echo/%%b
))>%root_path%%text_name_output%
)
)
pause
Using a batch file for this task is absolutely not appropriate, however, here's a basic structure which should do what you asked, however slow that may be.
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "InFile=text.txt"
Set "LineLimit=1000"
Set "OutFile=output_text.txt"
If Not Exist "%InFile%" Exit /B
(
For /F Delims^= %%G In (
'%SystemRoot%\System32\findstr.exe /N /R "^" "%InFile%" 2^>NUL'
) Do (
Set "}=%%G"
Set /A "{=%%G" 2>NUL
SetLocal EnableDelayedExpansion
If !{! Gtr %LineLimit% (
EndLocal
GoTo Next
)
Echo(!}:*:=!
EndLocal
)
) 1>"%OutFile%"
:Next
I figured it out if the text or document has more than 1000 lines then delete all lines that follow.
#ECHO OFF & setLocal EnableDelayedExpansion
color 0A
%*
SET root_path=%~dp0
set text_name=text.txt
set text_name_output=output_text.txt
set max_line_count=1000
set /a counter=0
for /f %%a in (%root_path%%text_name%) do set /a counter+=1
set /a "counter=%counter%-%max_line_count%"
more +%counter% "%root_path%%text_name%" >"%root_path%%text_name_output%"
move /y "%root_path%%text_name_output%" "%root_path%%text_name%" >nul
pause
It sounds like you want a head program. This is easily done in PowerShell.
Get-Content -Path '.\text.txt' -First 1000 >'.\output_text.txt'
If you must run from cmd.exe, the following might be used.
powershell -NoLogo -NoProfile -Command ^
"Get-Content -Path '.\text.txt' -First 1000 >'.\output_text.txt'"
This is a batch that pings the servers of a game I play, so that I am able to find the best server for me.
Is there a way I can have it ping all the servers then list them in order from the slowest response to the highest?
#TITLE OSRS Ping Checker
#ECHO off
SET usaworlds=5,6,7,13,14,15,19,20,21,22,23,24,29,30,31,32,37,38,39,40,45,46,47,48,53,54,55,56,57,61,62,69,70,74,77,78,86,117
#ECHO ---------------------------------------------------
#ECHO USA
#ECHO ---------------------------------------------------
FOR %%i IN (%usaworlds%) DO (
Echo | SET /p=World %%i
FOR /F "tokens=5" %%a IN ('Ping oldschool%%i.runescape.com -n 1 ^| FIND "time="') DO Echo %%a
)
PAUSE
#ECHO off
setlocal enabledelayedexpansion
TITLE OSRS Ping Checker
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
del x.tmp 2>nul
SET usaworlds=5,6,7,13,14,15,19,20,21,22,23,24,29,30,31,32,37,38,39,40,45,46,47,48,53,54,55,56,57,61,62,69,70,74,77,78,86,117
ECHO ---------------------------------------------------
ECHO USA
ECHO ---------------------------------------------------
FOR %%i IN (%usaworlds%) DO (
<nul set /p "=checking World %%i !CR!"
FOR /F "tokens=5" %%a IN ('Ping oldschool%%i.runescape.com -n 1 ^| FIND "TTL="') DO (
for /f "tokens=2 delims==" %%b in ("%%a") do (
set tim=00000%%b
set tim=!tim:~-7,-2!
)
)
echo !tim! World %%i>>x.tmp
)
for /f "tokens=3" %%c in ('sort /r x.tmp') do set fastest=%%c
echo fastest response from World %fastest%
PAUSE
Note: the time of a one-time ping isn't reliable (check the different times with ping -t) and so this approach may give you false results. better check "Average" with ping -n 5 or even higher, but that will of course decrease the performance of the script.
To make it faster and more reliable using the average times, you can run the pings in parallel (I have used that method years ago in another context)
#ECHO off
setlocal
TITLE OSRS Ping Checker
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
del %temp%\x.tmp 2>nul
SET usaworlds=wrgl,5,6,7,13,14,15,19,20,21,22,23,24,29,30,31,32,37,38,39,40,45,46,47,48,53,54,55,56,57,61,62,69,70,74,77,78,86,117
#ECHO ---------------------------------------------------
#ECHO USA
#ECHO ---------------------------------------------------
FOR %%i IN (%usaworlds%) DO (
start /min "Pinging" cmd /v:on /c "(FOR /F "tokens=9 delims= " %%a IN ('Ping oldschool%%i.runescape.com -n 5^|findstr /e "ms"') do set avrg= %%a)& >> %temp%\x.tmp echo ^!avrg:~-7,-2^!" World %%i
)
:wait
timeout 1 >nul
tasklist /FI "WINDOWTITLE eq Pinging" |find ".exe" >nul && goto :wait
for /f "tokens=3" %%c in ('sort /r %temp%\x.tmp') do set fastest=%%c
echo fastest response from World %fastest%
PAUSE
Using start /min instead of start /b does keep your screen clean in case of errors (makes it a bit slower, but you won't notice it)
Since there are many hosts to check/ping, sequencial processing lasts for a quite long time, particularly when following Stephan's recommendation of using more than one echo requests and taking the average reply time.
So I suggest to use a different approach and let the ping requests happen in simultaneous processes:
#title OSRS Ping Checker
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ARG=%~1" & if defined _ARG shift /1 & goto :DO_PING & ^
rem // (jump to label `DO_LOOP` in case arguments are provided)
set "_USAWORLDS=5,6,7,13,14,15,19,20,21,22,23,24,29,30,31,32,37,38,39,40,45,46,47,48,53,54,55,56,57,61,62,69,70,74,77,78,86,117"
set "_ECHOREQUS=10" & rem // (number of echo requests to send per host)
set "_TEMPFILEB=%TEMP%\%~n0_%RANDOM%" & rem // (path and base name of temporary files)
set "_FINALFILE=%~dpn0.txt" & rem // (path and full name of return file)
rem // Process items in sub-routine but in parallel processes:
for %%I in (%_USAWORLDS%) do (
rem // Redirect output of every process to individual temporary file:
> "%_TEMPFILEB%_%%~I.tmp" start /B "" cmd /C "%~f0" :DO_PING %%~I %_ECHOREQUS%
)
rem /* Wait until all temporary files are write-accessible, meaning that
rem all the parallel processes have been completed/terminated: */
:POLL
for %%I in (%_USAWORLDS%) do (
rem // Try appending nothing to check for write-access:
2> nul (>> "%_TEMPFILEB%_%%~I.tmp" rem/) || (
rem // Wait a bit to not overload the processor:
> nul timeout /T 1 /NOBREAK
goto :POLL
)
)
rem // Combine all individual temporary files into one:
> nul copy /Y "%_TEMPFILEB%_*.tmp" "%_TEMPFILEB%.tmp" /B
rem // Sort data as desired (alphabetic sorting):
sort /R "%_TEMPFILEB%.tmp" /O "%_TEMPFILEB%.tmp"
rem // Create return file, write header:
> "%_FINALFILE%" echo ms host
rem // Append sorted data to return file:
> nul copy /Y "%_FINALFILE%" + "%_TEMPFILEB%.tmp" "%_FINALFILE%" /B
rem // Clean up temporary files:
del "%_TEMPFILEB%_*.tmp" "%_TEMPFILEB%.tmp"
endlocal
exit /B
:DO_PING
rem // Build host URL to ping, set number of echo requests to send:
set "URL=oldschool%~1.runescape.com"
set /A "NUM=%~2"
rem /* Perform ping and capture last line of response, which should contain
rem the average reply time: */
set "STR="
for /F "delims=" %%P in ('2^> nul ping "%URL%" -n %NUM%') do set "STR=%%P"
rem // Check whether last line of response contains average reply time:
if not defined STR exit /B
set "AVG=%STR:*Average =%"
set "AVG=%AVG:~1%"
if "%AVG%"=="%STR%" exit /B
rem /* Convert average reply time to pure left-zero-padded number; the padding
rem is intended to simplify the later (purely alphabetic) sorting: */
set /A "AVG=AVG"
set "AVG=000000%AVG%"
rem // Return average reply time together with respective host URL:
echo %AVG:~-6% "%URL%"
exit /B
I was wondering...
I have a program, and I want to charge money for it.
it runs on Windows, and is written mostly in VB and Batch-files...
how can I force the user to buy a product key for it, and activate it to use the Paid Version?
Thanks in advance!
~ #Cascading-style
This just a little example showing you that you can limit the number of execution of your program , so if the maximum of number of execution is reached the program will Auto delete by him self
#echo off
Setlocal enabledelayedexpansion
Title Count the number of times my BATCH file is run
Mode Con Cols=70 lines=7 & color 0E
Set /a MaxExecution=3
set /a count=1
set "FileCount=%tmp%\%~n0.dll"
If Not exist "%FileCount%" (
echo !count! > "%FileCount%"
) else (
For /F "delims= " %%a in ('Type "%FileCount%"') Do (
set /a count=!count! + %%a
echo !count! > "%FileCount%"
)
)
echo.
echo This Program is running for "!count!" time(s)
Call :SelfDelete
pause>nul & Exit /b
::**************************************************************
:SelfDelete
echo.
If !count! GTR !MaxExecution! (
Color 0C
echo The maximum execution of this "%~nx0" is set to "!MaxExecution!"
echo and it is reached & Timeout /T 5 /Nobreak>nul & Del %~f0
) else (
echo The counting is "!count!" and the max is set to "!MaxExecution!"
)
Goto :EOF
::**************************************************************