mirror of
https://github.com/reactos/reactos.git
synced 2024-08-20 18:35:32 +00:00
572 lines
13 KiB
Batchfile
572 lines
13 KiB
Batchfile
@echo off
|
|
|
|
::
|
|
:: Some basic tests
|
|
::
|
|
|
|
echo ------------ Testing FOR loop ------------
|
|
echo --- Multiple lines
|
|
for %%i in (A
|
|
B
|
|
C) do echo %%i
|
|
|
|
echo --- Lines and spaces
|
|
for %%i in (D
|
|
E
|
|
F) do echo %%i
|
|
|
|
echo --- Multiple lines and commas
|
|
for %%i in (G,
|
|
H,
|
|
I
|
|
) do echo %%i
|
|
|
|
echo --- Multiple lines and %%I
|
|
:: The FOR-variable is case-sensitive
|
|
for %%i in (J
|
|
K
|
|
L) do echo %%I
|
|
|
|
echo --- Multiple lines and %%j
|
|
for %%i in (M,
|
|
N,
|
|
O
|
|
) do echo %%j
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
::
|
|
:: 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
|
|
|
|
|
|
::
|
|
:: 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
|
|
|
|
|
|
:: 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%
|
|
|
|
|
|
|
|
::
|
|
:: 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.
|