2017-11-16 23:56:01 +00:00
@ echo off
2020-07-29 16:04:05 +00:00
2020-07-29 20:31:23 +00:00
: :
: : Some basic tests
: :
2020-07-29 16:04:05 +00:00
echo ------------ Testing FOR loop ------------
echo --- Multiple lines
2017-11-16 23:56:01 +00:00
for %% i in ( A
B
C) do echo %% i
2020-07-29 16:04:05 +00:00
echo --- Lines and spaces
2017-11-16 23:56:01 +00:00
for %% i in ( D
E
F) do echo %% i
2020-07-29 16:04:05 +00:00
echo --- Multiple lines and commas
2017-11-16 23:56:01 +00:00
for %% i in ( G,
H,
I
) do echo %%i
2020-07-29 16:04:05 +00:00
echo --- Multiple lines and %% I
: : The FOR-variable is case-sensitive
2017-11-16 23:56:01 +00:00
for %% i in ( J
K
L) do echo %% I
2020-07-29 16:04:05 +00:00
echo --- Multiple lines and %% j
2017-11-16 23:56:01 +00:00
for %% i in ( M,
N,
O
) do echo %%j
2020-07-29 16:04:05 +00:00
2020-09-22 21:02:55 +00:00
echo --- FOR /F token parsing
: : This test requires extensions being enabled
setlocal enableextensions
set TEST_STRING = " _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ ? @ [ \ ] _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ ? @ [ \ ] "
set " ECHO_STRING=?= %% ? @= %% @ A= %% A B= %% B C= %% C D= %% D E= %% E F= %% F G= %% G H= %% H I= %% I J= %% J K= %% K L= %% L M= %% M N= %% N O= %% O P= %% P Q= %% Q R= %% R S= %% S T= %% T U= %% U V= %% V W= %% W X= %% X Y= %% Y Z= %% Z [= %% [ \= %% \ ]= %% ] ^ ^ = %% ^ ^ _= %% _ `= %% ` a= %% a b= %% b c= %% c d= %% d e= %% e f= %% f g= %% g h= %% h i= %% i j= %% j k= %% k l= %% l m= %% m n= %% n o= %% o p= %% p q= %% q r= %% r s= %% s t= %% t u= %% u v= %% v w= %% w x= %% x y= %% y z= %% z {= %% { ^ |= %% ^ | }= %% } ^ ~= %% ^ ~ "
echo .
: : Bug 1: Ranges that are not specified in increasing order are ignored.
: : Token numbers strictly greater than 31 are just ignored, and if they
: : appear in a range, the whole range is ignored.
for /f " tokens=30-32 " %% ? in ( %TEST_STRING% ) do echo %ECHO_STRING%
echo .
for /f " tokens=5-1 " %% ? in ( %TEST_STRING% ) do echo %ECHO_STRING%
echo .
: : Bug 2: Ranges that partially overlap: too many variables are being allocated,
: : while only a subset is actually used. This leads to the extra variables returning
: : empty strings.
for /f " tokens=1-31,31,31 " %% ? in ( %TEST_STRING% ) do echo %ECHO_STRING%
echo .
for /f " tokens=1-31,1-31 " %% ? in ( %TEST_STRING% ) do echo %ECHO_STRING%
echo .
for /f " tokens=1-5,3,5,6 " %% ? in ( %TEST_STRING% ) do echo %ECHO_STRING%
echo .
for /f " tokens=1-31,* tokens=1-31 tokens=1-20,* " %% ? in ( %TEST_STRING% ) do echo %ECHO_STRING%
echo .
: : For comparison, this works:
for /f " tokens=1-5,6 " %% ? in ( %TEST_STRING% ) do echo %ECHO_STRING%
echo .
for /f " tokens=1-5,6-10 " %% ? in ( %TEST_STRING% ) do echo %ECHO_STRING%
echo .
endlocal
2020-07-29 16:04:05 +00:00
echo ---------- Testing AND operator ----------
: : Test for TRUE condition - Should be displayed
ver | find " Ver " > NUL && echo TRUE AND condition
: : Test for FALSE condition - Should not display
ver | find " 1234 " > NUL && echo FALSE AND condition
echo ---------- Testing OR operator -----------
: : Test for TRUE condition - Should not display
ver | find " Ver " > NUL || echo TRUE OR condition
: : Test for FALSE condition - Should be displayed
ver | find " 1234 " > NUL || echo FALSE OR condition
2020-07-29 20:31:23 +00:00
: :
: : Testing CMD exit codes and errorlevels.
: :
: : Observations:
: : - OR operator || converts the LHS error code to ERRORLEVEL only on failure;
: : - Pipe operator | converts the last error code to ERRORLEVEL.
: :
: : See https://stackoverflow.com/a/34987886/13530036
: : and https://stackoverflow.com/a/34937706/13530036
: : for more details.
: :
setlocal enableextensions
echo ---------- Testing CMD exit codes and errorlevels ----------
: : Tests for CMD returned exit code.
echo --- CMD /C Direct EXIT call
call : setError 0
cmd /c " exit 42 "
call : checkErrorLevel 42
call : setError 111
cmd /c " exit 42 "
call : checkErrorLevel 42
echo --- CMD /C Direct EXIT /B call
call : setError 0
cmd /c " exit /b 42 "
call : checkErrorLevel 42
call : setError 111
cmd /c " exit /b 42 "
call : checkErrorLevel 42
: : Non-existing ccommand, or command that only changes
: : the returned code (but NOT the ERRORLEVEL) and EXIT.
echo --- CMD /C Non-existing command
: : EXIT alone does not change the ERRORLEVEL
call : setError 0
cmd /c " nonexisting & exit "
call : checkErrorLevel 9009
call : setError 111
cmd /c " nonexisting & exit "
call : checkErrorLevel 9009
call : setError 0
cmd /c " nonexisting & exit /b "
call : checkErrorLevel 9009
call : setError 111
cmd /c " nonexisting & exit /b "
call : checkErrorLevel 9009
echo --- CMD /C RMDIR (no ERRORLEVEL set)
call : setError 0
cmd /c " rmdir nonexisting & exit "
call : checkErrorLevel 0
call : setError 111
cmd /c " rmdir nonexisting & exit "
call : checkErrorLevel 0
call : setError 0
cmd /c " rmdir nonexisting & exit /b "
call : checkErrorLevel 0
call : setError 111
cmd /c " rmdir nonexisting & exit /b "
call : checkErrorLevel 0
: : Failing command (sets ERRORLEVEL to 1) and EXIT
echo --- CMD /C DIR (sets ERRORLEVEL) - With failure
: : EXIT alone does not change the ERRORLEVEL
call : setError 0
cmd /c " dir nonexisting>NUL & exit "
call : checkErrorLevel 1
call : setError 111
cmd /c " dir nonexisting>NUL & exit "
call : checkErrorLevel 1
call : setError 0
cmd /c " dir nonexisting>NUL & exit /b "
call : checkErrorLevel 1
call : setError 111
cmd /c " dir nonexisting>NUL & exit /b "
call : checkErrorLevel 1
: : Here EXIT changes the ERRORLEVEL
call : setError 0
cmd /c " dir nonexisting>NUL & exit 42 "
call : checkErrorLevel 42
call : setError 111
cmd /c " dir nonexisting>NUL & exit 42 "
call : checkErrorLevel 42
call : setError 0
cmd /c " dir nonexisting>NUL & exit /b 42 "
call : checkErrorLevel 42
call : setError 111
cmd /c " dir nonexisting>NUL & exit /b 42 "
call : checkErrorLevel 42
: : Succeeding command (sets ERRORLEVEL to 0) and EXIT
echo --- CMD /C DIR (sets ERRORLEVEL) - With success
call : setError 0
cmd /c " dir>NUL & exit "
call : checkErrorLevel 0
call : setError 111
cmd /c " dir>NUL & exit "
call : checkErrorLevel 0
call : setError 0
cmd /c " dir>NUL & exit 42 "
call : checkErrorLevel 42
call : setError 111
cmd /c " dir>NUL & exit 42 "
call : checkErrorLevel 42
call : setError 0
cmd /c " dir>NUL & exit /b 42 "
call : checkErrorLevel 42
call : setError 111
cmd /c " dir>NUL & exit /b 42 "
call : checkErrorLevel 42
: : Same sorts of tests, but now from within an external batch file:
: : Tests for CALL command returned exit code.
: : Use an auxiliary CMD file
mkdir foobar && cd foobar
: : Non-existing ccommand, or command that only changes
: : the returned code (but NOT the ERRORLEVEL) and EXIT.
echo --- CALL Batch Non-existing command
: : EXIT alone does not change the ERRORLEVEL
echo nonexisting ^& exit /b> tmp.cmd
call : setError 0
call tmp.cmd
call : checkErrorLevel 9009
echo nonexisting ^& exit /b> tmp.cmd
call : setError 111
call tmp.cmd
call : checkErrorLevel 9009
: : These tests show that || converts the returned error code
: : from RMDIR on failure, and converts it to an ERRORLEVEL
: : (first two tests: no ||, thus no ERRORLEVEL set;
: : last two tests: ||used and ERRORLEVEL is set).
: :
echo --- CALL Batch RMDIR (no ERRORLEVEL set)
: : This test shows that if a batch returns error code 0 from CALL,
: : then CALL will keep the existing ERRORLEVEL (here, 111)...
echo rmdir nonexisting> tmp.cmd
echo exit /b>> tmp.cmd
call : setError 0
call tmp.cmd
call : checkErrorLevel 0
echo rmdir nonexisting> tmp.cmd
echo exit /b>> tmp.cmd
call : setError 111
call tmp.cmd
call : checkErrorLevel 111
echo --- CALL Batch RMDIR with ^| ^| (sets ERRORLEVEL)
: : ... but if a non-zero error code is returned from CALL,
: : then CALL uses it as the new ERRORLEVEL.
echo rmdir nonexisting ^| ^| rem> tmp.cmd
echo exit /b>> tmp.cmd
call : setError 0
call tmp.cmd
call : checkErrorLevel 2
: : This gives the same effect, since the last command's error code
: : is returned and transformed by CALL into an ERRORLEVEL:
echo rmdir nonexisting> tmp.cmd
call : setError 0
call tmp.cmd
call : checkErrorLevel 2
echo rmdir nonexisting ^| ^| rem> tmp.cmd
echo exit /b>> tmp.cmd
call : setError 111
call tmp.cmd
call : checkErrorLevel 2
: : This gives the same effect, since the last command's error code
: : is returned and transformed by CALL into an ERRORLEVEL:
echo rmdir nonexisting> tmp.cmd
call : setError 111
call tmp.cmd
call : checkErrorLevel 2
: : Failing command (sets ERRORLEVEL to 1) and EXIT
echo --- CALL Batch DIR (sets ERRORLEVEL) - With failure
echo dir nonexisting^> NUL> tmp.cmd
call : setError 0
call tmp.cmd
call : checkErrorLevel 1
echo dir nonexisting^> NUL> tmp.cmd
call : setError 111
call tmp.cmd
call : checkErrorLevel 1
echo dir nonexisting^> NUL ^& goto :eof> tmp.cmd
call : setError 0
call tmp.cmd
call : checkErrorLevel 1
echo dir nonexisting^> NUL ^& goto :eof> tmp.cmd
call : setError 111
call tmp.cmd
call : checkErrorLevel 1
echo dir nonexisting^> NUL ^& exit /b> tmp.cmd
call : setError 0
call tmp.cmd
call : checkErrorLevel 1
echo dir nonexisting^> NUL ^& exit /b> tmp.cmd
call : setError 111
call tmp.cmd
call : checkErrorLevel 1
echo dir nonexisting^> NUL ^& exit /b 42 > tmp.cmd
call : setError 0
call tmp.cmd
call : checkErrorLevel 42
echo dir nonexisting^> NUL ^& exit /b 42 > tmp.cmd
call : setError 111
call tmp.cmd
call : checkErrorLevel 42
: : Succeeding command (sets ERRORLEVEL to 0) and EXIT
echo --- CALL Batch DIR (sets ERRORLEVEL) - With success
echo dir^> NUL> tmp.cmd
call : setError 0
call tmp.cmd
call : checkErrorLevel 0
echo dir^> NUL> tmp.cmd
call : setError 111
call tmp.cmd
call : checkErrorLevel 0
echo dir^> NUL ^& goto :eof> tmp.cmd
call : setError 0
call tmp.cmd
call : checkErrorLevel 0
echo dir^> NUL ^& goto :eof> tmp.cmd
call : setError 111
call tmp.cmd
call : checkErrorLevel 0
echo dir^> NUL ^& exit /b> tmp.cmd
call : setError 0
call tmp.cmd
call : checkErrorLevel 0
echo dir^> NUL ^& exit /b> tmp.cmd
call : setError 111
call tmp.cmd
call : checkErrorLevel 0
echo dir^> NUL ^& exit /b 42 > tmp.cmd
call : setError 0
call tmp.cmd
call : checkErrorLevel 42
echo dir^> NUL ^& exit /b 42 > tmp.cmd
call : setError 111
call tmp.cmd
call : checkErrorLevel 42
: : Cleanup
del tmp.cmd
cd .. & rmdir /s/q foobar
2020-07-27 14:42:42 +00:00
: :
: : ERRORLEVEL tests for special commands.
: :
: : For some commands, the errorlevel is set differently,
: : whether or not they are run within a .BAT, a .CMD, or
: : directly from the command-line.
: :
: : These commands are:
: : APPEND/DPATH, ASSOC, FTYPE, PATH, PROMPT, SET.
: :
: : See https://ss64.com/nt/errorlevel.html for more details.
: :
echo ---------- Testing ERRORLEVEL in .BAT and .CMD ----------
: : Use an auxiliary CMD file
mkdir foobar && cd foobar
echo --- In .BAT file
call : outputErrLvlTestToBatch tmp.bat
cmd /c tmp.bat
del tmp.bat
echo --- In .CMD file
call : outputErrLvlTestToBatch tmp.cmd
cmd /c tmp.cmd
del tmp.cmd
: : Cleanup
cd .. & rmdir /s/q foobar
: : Go to the next tests below.
goto : continue
: : ERRORLEVEL test helper function
: outputErrLvlTestToBatch (filename)
echo @echo off> %1
echo setlocal enableextensions>> %1
: : Reset the errorlevel
echo call :zeroErrLvl>> %1
: : dpath
: : echo %errorlevel%
: : dpath xxx
: : echo %errorlevel%
: : dpath
: : echo %errorlevel%
echo assoc^> NUL>> %1
echo echo %% errorlevel%% >> %1
echo assoc .nonexisting^> NUL>> %1
echo echo %% errorlevel%% >> %1
echo assoc .nonexisting^= ^> NUL>> %1
echo echo %% errorlevel%% >> %1
: : ftype
: : echo %errorlevel%
: : ftype xxx
: : echo %errorlevel%
: : ftype
: : echo %errorlevel%
echo path^> NUL>> %1
echo echo %% errorlevel%% >> %1
echo path^; ^> NUL>> %1
echo echo %% errorlevel%% >> %1
echo prompt^> NUL>> %1
echo echo %% errorlevel%% >> %1
echo prompt ^$ p^$ g^> NUL>> %1
echo echo %% errorlevel%% >> %1
echo prompt foobar^> NUL>> %1
echo echo %% errorlevel%% >> %1
echo set^> NUL>> %1
echo echo %% errorlevel%% >> %1
echo set nonexisting^> NUL>> %1
echo echo %% errorlevel%% >> %1
echo set nonexisting^= ^> NUL>> %1
echo echo %% errorlevel%% >> %1
echo set nonexisting^= trololol^> NUL>> %1
echo echo %% errorlevel%% >> %1
echo set nonexisting^= ^> NUL>> %1
echo echo %% errorlevel%% >> %1
echo goto :eof>> %1
: : Zero ERRORLEVEL
echo :zeroErrLvl>> %1
echo exit /B 0 >> %1
goto : eof
: :
: : Next suite of tests.
: :
: continue
2020-07-27 14:46:49 +00:00
: : Testing different ERRORLEVELs from the SET command.
: : See https://ss64.com/nt/set.html for more details.
echo ---------- Testing SET /A ERRORLEVELs ----------
echo --- Success
call : setError 0
set /a " total=1+1 "
call : checkErrorLevel 0
echo %errorlevel%
echo %total%
echo --- Unbalanced parentheses
call : setError 0
set /a " total=(2+1 "
call : checkErrorLevel 1073750988
echo %errorlevel%
echo %total%
echo --- Missing operand
call : setError 0
set /a " total=5* "
call : checkErrorLevel 1073750989
echo %errorlevel%
echo %total%
echo --- Syntax error
call : setError 0
set /a " total=7$3 "
call : checkErrorLevel 1073750990
echo %errorlevel%
echo %total%
echo --- Invalid number
call : setError 0
set /a " total=0xdeadbeeg "
call : checkErrorLevel 1073750991
echo %errorlevel%
echo %total%
echo --- Number larger than 32-bits
call : setError 0
set /a " total=999999999999999999999999 "
call : checkErrorLevel 1073750992
echo %errorlevel%
echo %total%
echo --- Division by zero
call : setError 0
set /a " total=1/0 "
call : checkErrorLevel 1073750993
echo %errorlevel%
echo %total%
2020-07-29 20:31:23 +00:00
: :
: : Finished!
: :
echo --------- Finished --------------
goto : EOF
: checkErrorLevel
if %errorlevel% neq %1 ( echo Unexpected errorlevel %errorlevel% , expected %1 ) else echo OK
goto : eof
: : Subroutine to set errorlevel and return
: : in windows nt 4.0, this always sets errorlevel 1, since /b isn't supported
: setError
exit /B %1
: : This line runs under cmd in windows NT 4, but not in more modern versions.