Loop Variable checking - string

Just trying to check if a function exists before trying to execute that Function.
I fear I may be trying to go to simple with this, I should probably be checking the string prior to executing the Process loop.
SET /P Selection=Please Select Options?
echo You chose: %Selection%
Call :Process
pause
goto :EOF
:Process
for %%A in (%Selection%) do (
if not exist :Opt%%A (Call :Redo) ELSE (Call :Opt%%A)
)
GOTO :EOF
:Redo
Echo %Selection%
SET /P Selection=Selection was Invalid, Please choose a Valid Option:
Call :Process
GOTO :EOF
:Opt1
ECHO Option 1's code
GOTO :EOF
So the problem I'm getting is that I seam to get stuck in if not exist statement and well is not allowing the functions to run correctly.
Thanks in advance

When a CALL command is executed with a non-existent label an error message is displayed, but the process continue with an ERRORLEVEL = 1 after the call. You may made good use of this point in order to avoid to check if the label exists:
EDIT: Code modified as reply to the comment
SET /P Selection=Please Select Options?
echo You chose: %Selection%
:Process
for %%A in (%Selection%) do (
Call :Opt%%A 2>NUL
if errorlevel 1 (
SET /P Selection=Selection was Invalid, Please choose a Valid Option:
goto :Process
)
)
pause
goto :EOF
:Opt1
ECHO Option 1's code
exit /B 0

Related

Check if userinput contains substring

Hello please help me with this one!
I would like to check if the user input URL contains the defined SUBSTRING or not.
If yes I would like to GOTO LONG else GOTO SHORT
Thank you!
#echo off
setlocal enabledelayedexpansion enableextensions
SET /P "URL= Input the link of the video: "
SET "SUBSTRING=?filter=archives&sort=time"
ECHO !URL! | FINDSTR /C:"!SUBSTRING!">nul
IF ERRORLEVEL 1 (GOTO SHORT) ELSE GOTO LONG
:LONG
SET LINK=1
ECHO THIS IS A LONG LINK
ECHO "THE LINK NUMBER IS %LINK%"
ECHO !URL!
GOTO END
:SHORT
SET LINK=0
ECHO THIS IS A SHORT LINK
ECHO "THE LINK NUMBER IS %LINK%"
ECHO !URL!
GOTO END
:END
pause
Using double quotes properly helps fix some of your problems. There shouldn't be any need to use delayed expansion if you use quotes for the echo as well.
#echo off
SET /P "URL=Input the link of the video:"
SET "SUBSTRING=?filter=archives"
ECHO "%URL%"|FINDSTR /C:"%SUBSTRING%">nul
IF ERRORLEVEL 1 (GOTO SHORT) ELSE GOTO LONG
:LONG
SET LINK=1
ECHO THIS IS A LONG LINK
ECHO "THE LINK NUMBER IS %LINK%"
GOTO END
:SHORT
SET LINK=0
ECHO THIS IS A SHORT LINK
ECHO "THE LINK NUMBER IS %LINK%"
GOTO END
:END
pause
Update Showing execution of batch file.
C:\Users\Squashman\Desktop>test.bat
Input the link of the video:https://www.twitch.tv/videos/578427308?filter=archives&sort=time
THIS IS A LONG LINK
"THE LINK NUMBER IS 1"
Press any key to continue . . .
So this is the working code. I will now mention the problems I had so others can learn from it
I must not enable setlocal enabledelayedexpansion
I have to use quotes around variables for the SET command
Because I have delayedexpansion disabled I must use percentages instead of exclamation marks when I want to use a variable in a command later
Correct me if I am wrong but thats what I figured out with these guys' help and on my own by trial and error
I made this list and updated the code so if a newbie like me gets stuck they can have a look at this post.
Once again thank you guys!
#echo off
SET /P "URL=Input the link of the video:"
SET "SUBSTRING=?filter=archives"
ECHO "%URL%"|FINDSTR /C:"%SUBSTRING%">nul
IF ERRORLEVEL 1 (GOTO SHORT) ELSE GOTO LONG
:LONG
SET LINK=1
ECHO THIS IS A LONG LINK
ECHO "THE LINK NUMBER IS %LINK%"
GOTO END
:SHORT
SET LINK=0
ECHO THIS IS A SHORT LINK
ECHO "THE LINK NUMBER IS %LINK%"
GOTO END
:END
pause

Parsing sets of variables from a string in a bat file

I have a situation where I'm trying to keep a static list of related items in a string and parse them out as sets in a bat file.
SET RootPath=C:\Users\woodh\test\
SET FromPath=StuffFrom\
SET ToPath=StuffTo\
SET CTLNames='text1.txt,red_text1:text2.txt,white_text2:text3.txt,blue_text3:'
With CTLNames containing pairs of entries to be parsed and consumed in the job.
I did the following
:Step20
rem -----------------------------------------------------------------
rem loop thru all files in the control list processing each pair at a time
rem -----------------------------------------------------------------
FOR /F "delims=:" %%f IN (%CTLNames%) DO (
IF NOT "%%f" == "" (
CALL:BreakEntry "%%f"
)
)
:Finish
rem ----------------------------------------------------------------
rem -- Finish
rem ----------------------------------------------------------------
goto end
:BreakEntry
rem -----------------------------------------------------------------
rem loop thru all files in the control list processing each entry one at a time
rem -----------------------------------------------------------------
Set EntryLine=%~1
IF NOT "%EntryLine%" == "" (
ECHO %EntryLine%
FOR /F "tokens=1,2 delims=," %%a IN ("%EntryLine%") DO (
ECHO %%a
ECHO %%b
CALL:MoveThisFile %%a, %%b
)
)
goto:eof
But It's only processing the first pair of names and not continuing through the rest of the list.
Your question is confusing. You didn't explained what exactly is the purpose of your code nor the expected output, so we can only guess. So I guess that you have a series of pairs of values separated by colon, and that each pair of values is separated by comma. This way, the problem with your code is that for /F command does not iterate over several values when just one string is processed: the string is divided accordingly to "tokens and delims" options and the command is executed just one time. You need to use a different method to process all substring in the string.
This is the way I would do it:
#echo off
setlocal
SET "CTLNames=text1.txt,red_text1:text2.txt,white_text2:text3.txt,blue_text3:"
for %%f in ("%CTLNames::=" "%") do (
for /F "tokens=1,2 delims=," %%a in (%%f) do (
echo %%a
echo %%b
echo CALL :MoveThisFile %%a, %%b
)
)
I suggest you to remove the #echo off line and execute the program, so you may review what exactly is executed.
The reason why it doesn't work as expected (it only prints the 1st pair), is because for /f works on lines; CTLNames only consists of a line so a single iteration is needed.
The confusing part is that it still printed the 1st pair...that is because it actually did the split (on the 1st :) but by default for only cares about the 1st token (before the delim) and drops the rest. You can convince yourself by changing the line to:
FOR /F "tokens=* delims=:" %%f IN (%CTLNames%) DO (
you'll see that the value of %%f (because we instructed it to take all the tokens into account) is the whole line.
The reason why I asked if the COLON(:) is mandatory as a separator between pairs, is because you can also iterate over a non numeric list - no /f flag, but here you can't specify the delimiter so you must use a regular one: SPACE( ), COMMA(,), SEMICOLON(;), TAB, and maybe others (anyway COLON is not one of them) - so this loop:
for %%f in (text1.txt:red_text1 text2.txt:white_text2 text3.txt:blue_text3;) do (
echo %%f
)
- note that I used 3 separators: TAB, SPACE and SEMICOLON in the for loop (not sure how visible it is) -
would yield:
text1.txt:red_text1
text2.txt:white_text2
text3.txt:blue_text3
Or you could use regular separators everywhere, and give up at the pair concept altogether, but I don't know if this is what you want.
I wasn't able to solve the problem using COLON as a separator from a single for loop, but I was able to find a way. Here's your script (slightly modified):
#ECHO OFF
rem ECHO %CTLNames%
CALL :Step20 "%CTLNames%"
GOTO :eof
:Step20
rem -----------------------------------------------------------------
rem loop thru all files in the control list processing each pair at a time
rem -----------------------------------------------------------------
IF "" == "%~1" GOTO :eof
FOR /F "tokens=1* delims=:" %%f IN ("%~1") DO (
rem echo f: %%f
CALL :BreakEntry "%%f"
CALL :Step20 "%%g%
)
GOTO :eof
:BreakEntry
rem -----------------------------------------------------------------
rem loop thru all files in the control list processing each entry one at a time
rem -----------------------------------------------------------------
Set EntryLine=%~1
ECHO %EntryLine%
FOR /F "tokens=1,2 delims=," %%a IN ("%EntryLine%") DO (
ECHO %%a
ECHO %%b
rem CALL :MoveThisFile %%a, %%b
)
GOTO :eof
The main thing is (besides other small changes) that Step20 is a recursive function (label), and it uses the for loop to split the line, it processes the 1st token, then it calls itself on the remaining tokens (until there are no more left).
Note: the single quotes surrounding CTLNames should be removed.

Random line of text using batch

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)

batch file renaming "This.is.a.FIlename.mp3" to "tiaf.mp3"

okay so I have a dir with some files. I want to do a specific file-renamingscript
i'm stuck with this part, taking only the first letter of each part of the filename:
if the filename would be
This.is.a.FIle.mp3
I would like to rename it to
tiaf.mp3
notice i want it to be all in lowercase.
The word length is variable so i cant take reference from it as a local variable !variable:~0,2!
anyone could help?
thanx!
edit: i forggot to ask. If you have an idea to make a test if the filename is of the format i mentioned. Because if the file is called. 'file.mp3' then i wouldn't want it to be renamed to 'f.mp3'
This should work, but if you want to allow also "!" exclamation marks in your filenames, it have to be a little bit extended.
#echo off
setlocal EnableDelayedExpansion
for %%f in ("C:\temp\folder\*.*") do (
call :createName "%%~f"
)
goto :eof
:: Compress a filename with more than one dot to only the first (lower) letters of each part
:: one.TWO.three.four.exe to ottf.exe
:createName <dot-filename>
setlocal
set "filename=#.%~n1"
set "ext=%~x1"
set "count=0"
set "short="
:createName.loop
for %%a in ("!filename!") do (
set "part=%%~xa"
set "filename=%%~na"
if defined part (
set /a count+=1
set "char=!part:~1,1!"
call :toLower char
set "short=!char!!short!"
) ELSE (
set "char="
)
rem echo "%%~na"-"%%~xa" "!char!" "!short!"
)
if defined part goto :createName.loop
set "short=!short!!ext!"
if !count! GTR 1 (
echo ren "%~f1" "!short!"
)
(
endlocal
goto :eof
)
:: convert a char to the lower variant or leave it unchanged if it isn't a char
:: use the %var:*n=% syntax to remove the front of a string, to get the correct char
:toLower <variable to char>
setlocal EnableDelayedExpansion
(
set "char=!%~1!"
set "helper=##aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz"
set "lower=!helper:*%char%=!"
set "lower=!lower:~0,1!"
if "!lower!"=="#" set "lower=!char!"
)
(
endlocal
set "%~1=%lower%"
goto :eof
)
Would this kind of logic work for you:
#echo off
for /f "delims=|" %%f in ('dir /b C:\temp') do call :runsub %%f
goto EOF
:runsub
for /f "tokens=1,2,3,4 delims=." %%a in ("%~n1") do set a=%%a&set b=%%b&set c=%%c&set d=%%d
if not "%a%"=="" echo %a%
if not "%b%"=="" echo %b%
if not "%c%"=="" echo %c%
if not "%d%"=="" echo %d%
:EOF
You can change the echo %a%, echo %b%, etc. to sets and get the substring from these. This also only gets the first 4 splits, you can add more if you need. Also change C:\temp to the appropriate directory.

Displaying lines from text file in a batch file

I'm tryin' to find a script that will let me display "linenumber# and linenumber# as well as lines#-#" from a text file in a batch file? I found this script here on this site..
#echo off
setlocal enabledelayedexpansion
if [%1] == [] goto usage
if [%2] == [] goto usage
SET /a counter=0
for /f "usebackq delims=" %%a in (%2) do (
if "!counter!"=="%1" goto exit
echo %%a
set /a counter+=1
)
goto exit
:usage
echo Usage: head.bat COUNT FILENAME
:exit
And it works great :) But it grabs the number of lines from the top of the text file. I want to be able to display certain lines in the text file, as well as a range if possible..
EG: I have a text file with 30 lines, and I want to display lines 1-4; 7-11; 13; 17-20; 22; 26 & 29
Here's a simple modification of the sample batch file above. Copy the code below to file and name it "LineDisplay.bat" - it takes the FirstLineNumber and LastLineNumber as parameters. Example: LineDisplay test.txt 12 30 (to read lines 12-30)
#echo off
setlocal enabledelayedexpansion
if [%1] == [] goto usage
if [%2] == [] goto usage
if [%3] == [] goto usage
set /a FirstLineNumber = %2
set /a LastLineNumber = %3
echo Reading from Line !FirstLineNumber! to !LastLineNumber!
SET /a counter=1
for /f "usebackq delims=" %%a in (%1) do (
if !counter! GTR !LastLineNumber! goto exit
if !counter! GEQ !FirstLineNumber! echo !counter! %%a
set /a counter+=1
)
goto exit
:usage
echo Usage: LineDisplay.bat FILENAME FirstLineNumber LastLineNumber
:exit
Here's a line to a nice tutorial on creating batch files http://vtatila.kapsi.fi/batch_tutorial.html
Seems to work:
#ECHO OFF
REM Show usage and quit if no file name was given
IF [%1]==[] GOTO USAGE
REM Show entire file if no range was given
IF [%2]==[] TYPE %1 & GOTO :EOF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET FILENAME=%1
SET LASTLINE=0
REM Build the array of lines to display
SHIFT
:RANGEREADLOOP
CALL :BUILDARRAY %1
SHIFT
IF NOT [%1]==[] GOTO RANGEREADLOOP
REM Loop through the file and keep track of the lines to display
SET CURRENTLINE=0
SET INDEX=1
FOR /F "delims=" %%l in (%FILENAME%) DO (
SET LINE=%%l
CALL :PRINTLINE
)
GOTO :EOF
REM Adds the lines from the specified range to the array of lines to display
:BUILDARRAY
REM Find out whether we have a single line or a range
SET TEST=%1
SET /A TEST1=%TEST%
SET /A TEST2=%TEST:-=%
IF %TEST1%==%TEST2% (
REM Single line
SET /A LASTLINE+=1
SET LINES[!LASTLINE!]=%1
) ELSE (
REM Range
FOR /F "tokens=1,2 delims=-" %%x IN ("%1") DO (SET RANGESTART=%%x&SET RANGEEND=%%y)
REM Some sanity checking
IF !RANGESTART! GTR !RANGEEND! (
ECHO.Problem with range !RANGESTART!-!RANGEEND!:
ECHO. Ranges must have a start value smaller than the end value.
EXIT /B 1
) ELSE (
FOR /L %%i IN (!RANGESTART!,1,!RANGEEND!) DO (
SET /A LASTLINE+=1
SET LINES[!LASTLINE!]=%%i
)
)
)
GOTO :EOF
REM Prints the specified line if the current line should be printed
:PRINTLINE
SET /A CURRENTLINE+=1
IF %CURRENTLINE%==!LINES[%INDEX%]! (
REM We have a line to print, so do this
ECHO !LINE!
SET /A INDEX+=1
)
GOTO :EOF
REM Prints usage and exits the batch file
:USAGE
ECHO.%~n0 - Displays selected lines from a text file.
ECHO.
ECHO.Usage:
ECHO. %~n0 ^<filename^> ^<range^> ...
ECHO.
ECHO. ^<filename^> - the text file from which to read
ECHO. ^<range^> - the line range(s) to display.
ECHO.
ECHO.Example:
ECHO. %~n0 foo.txt 1-4 13 15 18-20
ECHO.
ECHO. will display the first four lines from the file "foo.txt",
ECHO. the 13th and 15th as well as the lines 18 to 20.
ECHO.
ECHO.Line ranges are separated by comma, semicolon or space. If no range is given,
ECHO.the entire file is displayed.
EXIT /B 1
GOTO :EOF
The whole script could use some better error checking, examples of what not to do or where error checking is a bit wonky:
dl foo.txt 1-2-4 (prints lines 1-2 but no error message)
dl foo.txt -1 (error message that the range 1- isn't correct)
Other limitations:
Ranges must be sorted. With the current implementation there is no way to do something like dl foo.txt 7-8,1-2.
No line may be selected twice. Something like dl foo.txt 1,2,2-8,11-15 will stop after the second line.
No support for UNIX-style line endings (U+000A)
EDIT: Fixed an issue with files that contain parentheses.

Resources