#echo off
rem ffmpeg -i test.ts 2>&1 | find "Duration" > duration.txt
rem set /p duration=<duration.txt
set duration= Duration: 01:09:52.59, start: 0.136811, bitrate: 10740 kb/s
set /a Hours=%duration:~12,2%
set /a Mins=%duration:~15,2%
set /a Secs=%duration:~18,2%
set /a Duration_In_Seconds=(hours*3600)+(mins*60)+secs
echo %duration_In_seconds%
It is failing when I try to set "09" as a integer variable called mins.
Invalid number. Numeric constants are either decimal (17), hexadecimal (0x11), or octal (021).
I need to convert the strings to integers so I can use some basic math to work out duration in seconds
read the SET help (type HELP SET from a command prompt), and you will see that SET always interprets zero prefixed numbers as octal. There is no setting that will force base 10. Instead you must manipulate the string such that it does not start with zero.
There is no need to write to a temporary file. You can use FOR /F to parse the output, and it can also break the output into hour, minute, and second tokens.
I know of 2 simple methods to circumvent the leading 0.
One method is to prefix the value with a 1 and then use the mod (remainder) operator to extract the desired number. In your case, the hours will be restricted to a max value of 99. If you need to support up to 999 then you could prefix with 10 and take mod of 1000. Note that it is possible to perform multiple assignments with a single SET /A statement using the , to delimit each assignment. SET /A obeys the correct order of precedence, so parentheses are not needed.
#echo off
for /f "tokens=2-4 delims=:. " %%A in ('ffmpeg -i test.ts 2^>^&1 ^| find "Duration"') do (
set /a Hours=1%%A%%100, Mins=1%%B%%100, Secs=1%%C%%100, Duration_In_Seconds=Hours*3600+Mins*60+Secs
)
The other method is to use a FOR /F loop to strip off the leading zeros. I append a 1 and then divide by 10 so that a string of only zeros still gets processed.
#echo off
for /f "tokens=2-4 delims=:. " %%A in ('ffmpeg -i test.ts 2^>^&1 ^| find "Duration"') do (
for /f "delims=0" %%N in ("%%A1") do set /a Hours=%%N/10
for /f "delims=0" %%N in ("%%B1") do set /a Mins=%%N/10
for /f "delims=0" %%N in ("%%C1") do set /a Secs=%%N/10
set /a Duration_In_Seconds=Hours*3600+Mins*60+Secs
)
If you want, it is easy to extend the code to round the seconds up if the fractional second is greater than or equal to 0.5. Simply add the , as a delimiter to parse the extra token, and then a few math tricks to do the rounding.
#echo off
for /f "tokens=2-5 delims=:., " %%A in ('ffmpeg -i test.ts 2^>^&1 ^| find "Duration"') do (
for /f "delims=0" %%N in ("%%A1") do set /a Hours=%%N/10
for /f "delims=0" %%N in ("%%B1") do set /a Mins=%%N/10
for /f "delims=0" %%N in ("%%C%%D1") do set /a Secs=(%%N+500)/1000
set /a Duration_In_Seconds=Hours*3600+Mins*60+Secs
)
using hexadecimal to decimal conversion, the following code works for figures inside range [0..39999]:
set str=000039999
set /a dgt=0x!str!
set /a "dgt=(10000*(dgt&983040)>>16)+(1000*(dgt&61440)>>12)+(100*(dgt&3840)>>8)+(10*(dgt&240)>>4)+(dgt&15)"
echo str="!str!"
echo dgt="!dgt!"
Related
I am looking for a short .bat script that can pull a partial file name and set it as a variable. The file names will consistently be the following structure, however there maybe more than 1 files at a time:
B06XTJ8RX3.MAIN.PC_430.jpg
or
B06XTJ8RX3.MRG1.PC_430.jpg
While there may be multiple files, the first 10 characters will be the same.
I want to name 'B06XTJ8RX3' as the variable. So far I have only been able to do this by writing the file names up to the first '.' into a text document and then pulling the contents of the text file removing the duplicate strings. All of that seems unnecessary.
set images=C:\file_path\images\
for /F "tokens=1 delims=." %%A IN ('dir /b %images%') do echo %%A >> dupes.txt
type NUL > asins.txt
for /F %%i in ('type dupes.txt ^| sort') Do (
If NOT "!prev!" == "%%i" (echo %%i>>asins.txt)
set prev=%%i
)
type NUL > dupes.txt
set prev=5000
Does anyone have a simple solution for this?
for /f "tokens=2delims==" %%x in (`set # 2^>nul`) do set "%%x="
set images=C:\file_path\images\
for /F "tokens=1 delims=." %%A IN ('dir /b %images%') do set /a "#%%A"+=1
set #
should report
#B06XTJ8RX3=2
for the above filenames (#firstelement=occurrences)
The for /f in the first line is intended to remove all variables starting # from the environment.
With each string detected, increment the count of #string found.
for /f "tokens=1,2delims==" %%x in (`set # 2^>nul`) do if "%%y" neq 1 echo %%x
should report each name with more than 1 file found.
If you guarantee that of the n files in the directory, the first 10 chars will all be the same and the 11th will be . (and therefore there are no files that do not fit this pattern) then
set images=C:\file_path\images\
set /a count=0
for /F "tokens=1 delims=." %%A IN ('dir /b /a-d %images%') do set "name=%%A"&set/a count+=1
echo the %count% files start "%name%"
use the /a-d switch to turn off directorynames, should they exist.
count them as a free bonus.
Please, tell me what is wrong in my batch-script? The task is to compare files names in the specific folder with an integer variable. There are two issues:
Files names consist of numbers and may contain spaces and leading zeroes (e.g. 01.cmd, 0 02.cmd, 0 0 3.cmd, e.t.c.), which, I suppose, have to be deleted for further comparison with an integer variable.
Convert files names variable from string type to integer to compare with an integer variable.
Here is the text of the script:
SetLocal EnableDelayedExpansion
for /f "usebackq delims=" %%i in (`dir /b C:\tmp`) do
rem delete spaces in the files names
echo %%i
set i=%%i: =%
echo %%i &::this always returns me only the same file name
rem int - is an integer variable to compare with
rem in this way I tried to convert a file name to integer
if %int% lss %%i set /a int=%%i: =% 2>nul
In Linux Shell it would be much easy, but I'm not so familiar with batch scripting. And this script need to be run under Windows OS.
The filename variable can have a "name only" specifier; %%~nf. I assume you would want to omit directory names. The resulting number is in the !NUM! variable.
#Stephan is correct that numbers with leading zeros are considered octal. I have revised the script to remove leading zeros.
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F "usebackq tokens=*" %%f IN (`DIR /A:-D /B`) DO (
SET "FN=%%~nf"
SET "NSN=!FN: =!x"
FOR /F "tokens=* delims=0" %%s in ("!NSN!") DO (SET "NSN=%%s")
SET "NSN=!NSN:~0,-1!"
SET /A "NUM=!NSN!"
ECHO NUM is !NUM!
)
The below script provides the output of each occurence of the token# 1 field but I need add two more conditions.
a. Output should be provided.i.e. only when it is more than one since I have millions of records in a file
b. if there are mulitple strings.i.e. combination of Key fields in a row needs to checked across all the lines for duplicates in a file.
#ECHO OFF
SETLOCAL enabledelayedexpansion
FOR %%c IN ($ #) DO FOR /f "delims==" %%i IN ('set %%c 2^>nul') DO
"SET %%i="
SET /a count=0
FOR /f "tokens=1delims=|" %%i IN (fscif.txt) DO (
SET /a count+=1
IF DEFINED $%%i (SET "$%%i=!$%%i! & !count!") ELSE (SET "$%%i=!count!")
SET /a #%%i+=1 )
FOR /f "tokens=1*delims=$=" %%i IN ('set $ 2^>nul') DO ( ECHO %%i;!#%%i! times;line no %%j
)
For Example:
Original File (Considering token 1 & 3 are key fields)
123|12|Jack
124|23|John
123|14|Jack
125|15|Sam
125|66|Sam
125|66|Sam
Ouput file:
123|Jack;2 times;line no 1 & 3
125|Sam;3 times;line no 4 & 5 & 6
#ECHO OFF
SETLOCAL enabledelayedexpansion
:: Temporary filename
:tloop
SET "temppfx=%temp%\%random%"
IF EXIST "%temppfx%*" GOTO tloop
:: Hold that tempfile name...
ECHO.>"%temppfx%_"
:: a long string of spaces note the end-of-string quote -----here--v
SET "spaces= "
SET /a count=0
(
FOR /f "tokens=1,3 delims=|" %%a IN (fscif.txt) DO (
SET /a count+=1
SET "field1=%%a%spaces%"
SET "field3=%%b%spaces%"
SET "fieldc=%spaces%!count!"
ECHO(!field1:~0,10!!field3:~0,12!^|!fieldc:~-8!^|!count!^|%%a^|%%b
)
)>"%temppfx%1"
:: Now report
SET "key=x"
SET /a count=0
(
FOR /f "tokens=1,3* delims=|" %%a IN ('sort "%temppfx%1" ') DO (
IF "!key!"=="%%a" (
SET "line=!line! %%b"
SET /a count+=1
) ELSE (IF !count! neq 0 CALL :output
SET key=%%a
SET line=%%b
SET "data=%%c"
SET /a count=1
)
)
CALL :output
)>report.txt
del "%temppfx%*"
GOTO :eof
:output
ECHO(!data!;%count% times;line nos %line: = ^& %
GOTO :eof
As I explained earlier, with millions of records, you are likely to run out of environment space. As posted above, I reckon you may still run out because the report of line numbers may be huge - no idea - you are familiar with your real data.
Essentially, the first thing to do is to establish a temporary file.
Starting with the tokens required in the input file - I followed 1 and 3 but no doubt there may be more - just follow the bouncing ball...
The selected fields are padded - on the right for text fields and on the left for the count field using the spaces variable.
Then the tempfile output is generated. I randomly chose a maximum length of 10 for the first field and 12 for the second. These two are combined to give the key field. The leading-filled count field is output as the second column so that after SORTing, the data will appear grouped by key, then line number. The remaining columns of interest are then reproduced.
The data is then sorted as input to the next for/f loop - only tokens 1 (the key), 3 (the raw line number) and "the rest" (the key without the padding) are of interest
Then it's simply a matter of counting matching keys and accumulating the line number in line and reporting when the key changes. One last output is required to report the very last data item, and we're done.
For this ugly batch job I recommend to use sed and uniq from the GNUWin Project:
#echo off&setlocal enabledelayedexpansion
set "inputfile=file"
set "outputfile=out"
set "tempfile=%temp%\%random%"
<"%inputfile%" sed "s/|.*|/|.*|/"|sort|uniq -d>"%tempfile%"
(for /f "usebackqtokens=1-3delims=|" %%i in ("%tempfile%") do (
set /a cnt=0&set "line="
for /f "delims=:" %%a in ('findstr /nr "%%i|%%j|%%k" "%inputfile%"') do set "line=!line!%%a & "&set /a cnt+=1
echo(%%i^|%%k;!cnt! times;line no !line:~0,-3!
))>"%outputfile%"
del "%tempfile%"
type "%outputfile%"
.. output is:
123|Jack;2 times;line no 1 & 3
125|Sam;3 times;line no 4 & 5 & 6
The Batch file below do what you want:
#echo off
setlocal EnableDelayedExpansion
rem Assemble "tokensValues" and "lastToken" variables from the parameters
set letters=0abcdefghijklmnopqrstuvwxyz
set tokensValues=%%!letters:~%1,1!
set lastToken=%1
:nextArg
shift
if "%1" equ "" goto endArgs
set "tokensValues=!tokensValues!#%%!letters:~%1,1!"
set lastToken=%1
goto nextArg
:endArgs
rem Accumulate duplicated strings
set line=0
for /F "tokens=1-%lastToken% delims=|" %%a in (fscif.txt) do (
set /A line+=1
if not defined lines[%tokensValues%] (
set lines[%tokensValues%]=!line!
) else (
set "lines[%tokensValues%]=!lines[%tokensValues%]! & !line!"
)
set /A times[%tokensValues%]+=1
)
rem Show the result
for /F "tokens=2* delims=[]=" %%a in ('set lines[ 2^>NUL') do (
if !times[%%a]! gtr 1 (
set string=%%a
set "string=!string:#=|!"
echo !string!;!times[%%a]! times;line no %%b
)
)
You must provide the number of the desired key fields in the parameters. For example, to consider 1 & 3 as key fields:
prog.bat 1 3
You may provide a maximum of 26 key fields with positions from 1 to 26; this limit may be easily increased up to 52.
This Batch file does not use any external command and works over the original file, so it should run fast. If the file is large, a sort or findstr command over it will take too long (even a simple copy, for that matter).
If we take your example data as representative of the real data, lines variable should store about 2500-3000 lines (that is, number of different lines where the same key fields appear), and with a total environment space of 64 MB I think this program will be capable of process your large files.
I have this:
#echo off
setlocal ENABLEDELAYEDEXPANSION
set bla="-666"
set zzz="333"
if !zzz! LSS !bla! echo lesser
pause
Gives me "lesser" which is wrong. I know this is because of the ASCII values. But what is the way around to compare them as if they are numbers?
EDIT:
Some more codes:
set mn=99999
for /f "tokens=1-9" %%a in (%%g) do (
set zzz=%%d
)
if "!zzz!" LSS "!mn!" set mn=!zzz!
None of the answers below gave me the correct result so far...
This works for me
#echo off
set bla=-666
set zzz=333
if %zzz% LSS %bla% echo lesser
pause >nul
No need for quotes or delayed expansion.
This works for me, removing the quotes and then comparing.
#echo off
setlocal ENABLEDELAYEDEXPANSION
set bla="-666"
set zzz="333"
for %%a in (!bla!) do set /a bla=%%~a+0
for %%a in (!zzz!) do set /a zzz=%%~a+0
echo !bla! !zzz!
if !bla! LSS !zzz! (
echo lesser
) else (
echo greater
)
pause
You are taking incorrect use of the blackquotes when setting a var, the VAR="CONTENT" are only for special cases, but not this.
#echo off
set /A "bla=-666"
set /A "zzz=333"
if %zzz% LSS %bla% echo lesser
pause
Notice i've used blackquotes to enclose the variable, the content don't have any blackquote.
And remember too to NOT use blackquotes in the comparision when comparing numbers, like this:
if "%zzz%" LSS "%bla%" echo lesser
How could one select a random line of text from a text file and set it in a variable to use?
The Batch program below is Eitan's solution slightly modified to run faster:
#echo off
setlocal EnableDelayedExpansion
set INPUT_FILE="test.txt"
:: # Count the number of lines in the text file and generate a random number
for /f "usebackq" %%a in (`find /V /C "" ^< %INPUT_FILE%`) do set lines=%%a
set /a randnum=%RANDOM% * lines / 32768 + 1, skiplines=randnum-1
:: # Extract the line from the file
set skip=
if %skiplines% gtr 0 set skip=skip=%skiplines%
for /f "usebackq %skip% delims=" %%a in (%INPUT_FILE%) do set "randline=%%a" & goto continue
:continue
echo Line #%randnum% is:
echo/!randline!
Like it is already mentioned here in StackOverflow, among others, %RANDOM% expands to a random number between 0 and 32767.
You can use this mechanism to generate a random line number. However, to make it a valid line number you will have to normalize it by the the number of lines in the input text file.
Here's a simple script that shows how to do it:
#echo off
setlocal enabledelayedexpansion
set INPUT_FILE="test.txt"
:: # Count the number of lines in the text file and generate a random number
set lines=0
for /f "usebackq" %%a in (%INPUT_FILE%) do set /a lines+=1
echo %RANDOM% >nul
set /a randnum=%RANDOM% * !lines! / 32768 + 1
:: # Extract the line from the file
set lines=0
for /f "usebackq tokens=*" %%a in (%INPUT_FILE%) do (
set /a lines+=1
if !lines!==!randnum! set randline=%%a
)
echo Line #!randnum! is:
echo.!randline!
Here's yet another approach. It reads the file name from the command line and uses a FOR /L loop to get to the calculated line number:
#ECHO OFF
FOR /F "" %%I IN ('FIND /C /V "" ^<%1') DO SET /A lines=%%I
SET /A skip=%RANDOM%%%lines
<%1 (
FOR /L %%I IN (1,1,%skip%) DO (
SET /P line=
)
SET line=
SET /P line=
)
ECHO(%line%
The FOR /F loop simply gets the number of lines in the file (the method is borrowed from #Aacini's answer).
A rather simplistic formula then calculates the number of lines to skip in the file.
Next, the file is read. The FOR /L loop merely consumes the specified number of lines using a SET /P instruction. Following the loop, one more SET /P command reads the line that is eventually ECHOed.
The above implementation is just to show the basic idea. It is not without issues, but some of them could easily be resolved:
There's no testing whether the parameter is indeed supplied. If it is absent, the script will break. You could add the necessary check at the beginning of the script like this:
IF "%~1"=="" GOTO :EOF
If there's no parameter, this command terminates the script by sending the control to the end of the script (GOTO :EOF).
The file specified might not exist. Again, you could test that at the beginning, just after verifying that the parameter is supplied, to terminate the script if necessary:
IF NOT EXIST %1 GOTO :EOF
If the file is empty, the lines will be 0 and the subsequent expression using it will run into a division by zero error. Therefore, you'll also need to test the resulting line count (and prevent the script from running further if the count is indeed 0). You can do that by adding the following line just after the FOR /F loop:
IF %lines%==0 GOTO :EOF
Like I said, the formula is somewhat simplistic. It doesn't produce a number greater than 32767, which is the limitation of %RANDOM%. That might well be enough for you, but in case it is not, you could extend the range to 230-1 using two %RANDOM% calls like this:
SET /A skip=(%RANDOM%*32768+%RANDOM%)%%lines
So, here's the same script again, amended to address the above mentioned issues:
#ECHO OFF
IF "%~1"=="" GOTO :EOF
IF NOT EXIST %1 GOTO :EOF
FOR /F "" %%I IN ('FIND /C /V "" ^<%1') DO SET /A lines=%%I
IF %lines%==0 GOTO :EOF
SET /A skip=(%RANDOM%*32768+%RANDOM%)%%lines
<%1 (
FOR /L %%I IN (1,1,%skip%) DO (
SET /P line=
)
SET line=
SET /P line=
)
ECHO(%line%
One other note is that, if you like, you can add messages explaining the reason for the premature termination of the script. Basically, wherever you want to add the message, you'll just need to replace the single
GOTO :EOF
with
(ECHO your message & GOTO :EOF)
For instance:
IF NOT EXIST %1 (ECHO Error! File not found & GOTO :EOF)