I wonder is there any way to check that if a label exist in a batch file?
If %input%=ABC (
If Label ABC Exists (
Goto ABC
)
)
How
If you add this to the top of your function:
if "%~1" == "test" goto:eof
You can use this to check if your function exists:
call :myFunction test 2>nul
if %errorlevel% == 0 call :myFunction
Example:
call :myFunction test 2>nul
if "%errorlevel%" == "0" (call :myFunction) else (echo function does not exist)
goto:eof
:myFunction
if "%~1" == "test" goto:eof
echo Function exists
goto:eof
first I would just rethink everything and every time I created a label, I'd go to the top in a "constants or variables area" and predefine each of the labels I had in the file... so after writing, I'd go and manually check all the labels and just do a
for %%l in ( :label1 :label2 :label3 :label4 :EOF ) do set x_labelexistconst_%%l=.
where :label1 :label2 :label3 are all eyeballed and manually entered -- think of it all as if CMD required items to be declared before use like many languages do, but if really lazy you can automate this with findstr as they have with the subroutine examples and have a line like this instead
for /F "tokens=1" %%l in ( 'findstr /i /r /c:"^[ ]*:[a-z0-9]*" "%~f0"' ) do set x_labelexistconst_%%l=.
in the code either of those loaded array of variables would it would be used like
if defined x_labelexistconst_%input% goto %input%
if defined x_labelexistconst_%input% call %input%
… the advantages of this methodology would be that there wouldn't be a CALL and then new findstr "EACH AND EVERYTIME" you did a "if exist label", but instead you would just do it "ONCE" at the beginning to preload the list/array/construct of labels.
I think this option also has the advantage of being able to do && || after the goto call because error levels aren't used to check for labels so
if defined x_labelexistconst_%input% (call %input% && echo call ok || echo call bad)
Ultimately searching file with findstr and CALL and GOTO (which reread the original bat file or something) are both very slow commands and avoiding their use or limiting their use to once or never is the best option for bat files -- especially over slow network connections where the bat file may be large. And it makes the entire script smaller simpler and easier to understand.
I didn't debug this or test this lots but it just seems like a better idea.
Here is a refined, more robust version of the MC ND answer. (The original answer, his edit addresses many of these same points).
Labels are case insensitive, so the search should be case insensitive.
A valid label may have additional text after the label, so there are two searches required. The additional text is frequently used as documentation. For example: :label documentation
is still a valid label.
findstr /ri /c:"^ *:%input% " /c:"^ *:%input%$" "%~f0" >nul 2>nul && goto %input%
The above should work in most situations, but there are a few unlikely conditions that could cause it to fail.
Any of the following characters can appear before the label - ,
;
=
<space>
<tab>
<0x255>
. They all are treated as spaces when they precede a label. But the search above only allows for <space>
. A [class]
expression could be used, but including <tab>
and <0x255>
can be awkward.
In a similar fashion, the label can be terminated by some characters other than <space>
(a different list).
The label could contain regular expression meta-characters.
The FINDSTR $
anchor only recognizes <CR><LF>
as end of line, so the search can fail if the script uses Unix style <LF>
line endings.
The search could be refined to handle most of the above conditions. But it is simpler to simply avoid those conditions in your code. I don't think it is possible to define a bullet proof search using a single FINDSTR. A bullet proof search would require at least two FINDSTRs, and one would have to use the /G:file
option - yuck.
Try the following code:
echo off
set "label=sub"
REM next line to reset errorlevel to zero:
(call )
call :%label% 2>nul || (echo %label% not found & exit /b 1)
echo back from %label%
Exit /b 0
:sab
echo here we are
findstr /i /r /c:"^[ ]*:%input%\>" "%~f0" >nul 2>nul && goto %input%
Search the label in the current batch file and if no errorlevel, label exists
EDITED - I realized there was an error on the way i was handling the end of the label and was going to edit the answer (it has been edited anyway) and i see the dbenham aclarations. He saw the error and corrected it. Thank you. Nice answer as always, BUT this is worse than what you have exposed.
In this moment i have only a XP to test, but this is what works for me. If anyone can test on later windows versions, please.
First problem: the start of the label. As usual dbenham is correct, and any character in the set [;=,<space><tab>0xFF]
can precede, single or repeated, the colon of the label. But, as far as it is the first character on the line, and it does not repeat, almost any character can precede the colon of the label (one exception is other colon). So, the following will work without problems
call :test
goto :test
echo this will not be echoed
X=;=:test
echo Hello
NO, this it not a valid line, if the parser try to execute the label line, a "command not recognized" error will happen, BUT is a valid label to call or goto.
Second problem: end of the label. As dbenham identified, most of us place a space and the list of arguments when the label is used to define a function/procedure. This was the error i realized and what has been corrected in my original answer. BUT, a space (and obviously the end of line) is not the only allowed characters after the label name. So, In the previous sample, any of the following labels will work
:test arguments
:test:arguments
:test>arguments
:test<arguments
:test&arguments
And yes,in this case they are valid commands to the parser and are valid labels
AND, of course, the two "problems" can happen at the same time
call :test
goto :test
echo this will not be echoed
< ;;:test:;; > This WORKS
echo Hello
POST EDIT 1 - It seems all this work was done years ago at dostips.com. Thanks to all who compiled the exaustive list referenced in comments. Next time, i'll search first.
POST EDIT 2 - I've been trying to deal with the limitations of findstr to include all cases. Well, there is no way. There are too many limitations, starting with the impossibility of include the 0xff character in a regular expression.
For a robust and simple solution, the answer from dbenham is the best option.
For a more robust, but STILL INCOMPLETE, no bulletproof version, and more complex than dbenham's answer
@echo off
for /l %%i in (1 1 10) do (
call :testLabelExist "test%%i" && echo Label [test%%i] exist || echo Label [test%%i] does not exist
)
exit /b
:test1
:test2
:test3
x:test4
::test5
:test6:
:test7#
:test8 parameters
:test9 parameters
:test10:myData
:testLabelExist
for /f "delims=" %%t in (
'forfiles /p "%~dp0." /m "%~nx0" /c "cmd /d /c @echo 0x09"'
) do (
findstr /i /m /r /c:"^[^:]*[ %%t]*:%~1[ %%t:;,=+]" /c:"^[^:]*[ %%t]*:%~1$" "%~f0" >nul 2>nul
)
exit /b %errorlevel%
And it still leaves out quoted label names, just to name one failure point.