7 echo ------------ Testing FOR loop ------------
8 echo --- Multiple lines
13 echo --- Lines and spaces
18 echo --- Multiple lines and commas
24 echo --- Multiple lines and %%I
25 :: The FOR-variable is case-sensitive
30 echo --- Multiple lines and %%j
36 echo --- FOR /F token parsing
37 :: This test requires extensions being enabled
38 setlocal enableextensions
40 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 { | } ~ ? @ [ \ ]"
41 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 {=%%{ ^|=%%^| }=%%} ^~=%%^~"
45 :: Bug 1: Ranges that are not specified in increasing order are ignored.
46 :: Token numbers strictly greater than 31 are just ignored, and if they
47 :: appear in a range, the whole range is ignored.
49 for /f "tokens=30-32" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
51 for /f "tokens=5-1" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
54 :: Bug 2: Ranges that partially overlap: too many variables are being allocated,
55 :: while only a subset is actually used. This leads to the extra variables returning
58 for /f "tokens=1-31,31,31" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
60 for /f "tokens=1-31,1-31" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
62 for /f "tokens=1-5,3,5,6" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
64 for /f "tokens=1-31,* tokens=1-31 tokens=1-20,*" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
67 :: For comparison, this works:
68 for /f "tokens=1-5,6" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
70 for /f "tokens=1-5,6-10" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
76 echo ---------- Testing AND operator ----------
77 :: Test for TRUE condition - Should be displayed
78 ver | find "Ver" > NUL && echo TRUE AND condition
80 :: Test for FALSE condition - Should not display
81 ver | find "1234" > NUL && echo FALSE AND condition
83 echo ---------- Testing OR operator -----------
84 :: Test for TRUE condition - Should not display
85 ver | find "Ver" > NUL || echo TRUE OR condition
87 :: Test for FALSE condition - Should be displayed
88 ver | find "1234" > NUL || echo FALSE OR condition
93 :: Testing CMD exit codes and errorlevels.
96 :: - OR operator || converts the LHS error code to ERRORLEVEL only on failure;
97 :: - Pipe operator | converts the last error code to ERRORLEVEL.
99 :: See https://stackoverflow.com/a/34987886/13530036
100 :: and https://stackoverflow.com/a/34937706/13530036
103 setlocal enableextensions
105 echo ---------- Testing CMD exit codes and errorlevels ----------
107 :: Tests for CMD returned exit code.
109 echo --- CMD /C Direct EXIT call
113 call :checkErrorLevel 42
117 call :checkErrorLevel 42
119 echo --- CMD /C Direct EXIT /B call
123 call :checkErrorLevel 42
127 call :checkErrorLevel 42
129 :: Non-existing ccommand, or command that only changes
130 :: the returned code (but NOT the ERRORLEVEL) and EXIT.
132 echo --- CMD /C Non-existing command
134 :: EXIT alone does not change the ERRORLEVEL
136 cmd /c "nonexisting & exit"
137 call :checkErrorLevel 9009
140 cmd /c "nonexisting & exit"
141 call :checkErrorLevel 9009
144 cmd /c "nonexisting & exit /b"
145 call :checkErrorLevel 9009
148 cmd /c "nonexisting & exit /b"
149 call :checkErrorLevel 9009
151 echo --- CMD /C RMDIR (no ERRORLEVEL set)
154 cmd /c "rmdir nonexisting & exit"
155 call :checkErrorLevel 0
158 cmd /c "rmdir nonexisting & exit"
159 call :checkErrorLevel 0
162 cmd /c "rmdir nonexisting & exit /b"
163 call :checkErrorLevel 0
166 cmd /c "rmdir nonexisting & exit /b"
167 call :checkErrorLevel 0
169 :: Failing command (sets ERRORLEVEL to 1) and EXIT
170 echo --- CMD /C DIR (sets ERRORLEVEL) - With failure
172 :: EXIT alone does not change the ERRORLEVEL
174 cmd /c "dir nonexisting>NUL & exit"
175 call :checkErrorLevel 1
178 cmd /c "dir nonexisting>NUL & exit"
179 call :checkErrorLevel 1
182 cmd /c "dir nonexisting>NUL & exit /b"
183 call :checkErrorLevel 1
186 cmd /c "dir nonexisting>NUL & exit /b"
187 call :checkErrorLevel 1
189 :: Here EXIT changes the ERRORLEVEL
191 cmd /c "dir nonexisting>NUL & exit 42"
192 call :checkErrorLevel 42
195 cmd /c "dir nonexisting>NUL & exit 42"
196 call :checkErrorLevel 42
199 cmd /c "dir nonexisting>NUL & exit /b 42"
200 call :checkErrorLevel 42
203 cmd /c "dir nonexisting>NUL & exit /b 42"
204 call :checkErrorLevel 42
206 :: Succeeding command (sets ERRORLEVEL to 0) and EXIT
207 echo --- CMD /C DIR (sets ERRORLEVEL) - With success
210 cmd /c "dir>NUL & exit"
211 call :checkErrorLevel 0
214 cmd /c "dir>NUL & exit"
215 call :checkErrorLevel 0
218 cmd /c "dir>NUL & exit 42"
219 call :checkErrorLevel 42
222 cmd /c "dir>NUL & exit 42"
223 call :checkErrorLevel 42
226 cmd /c "dir>NUL & exit /b 42"
227 call :checkErrorLevel 42
230 cmd /c "dir>NUL & exit /b 42"
231 call :checkErrorLevel 42
234 :: Same sorts of tests, but now from within an external batch file:
235 :: Tests for CALL command returned exit code.
237 :: Use an auxiliary CMD file
238 mkdir foobar && cd foobar
240 :: Non-existing ccommand, or command that only changes
241 :: the returned code (but NOT the ERRORLEVEL) and EXIT.
243 echo --- CALL Batch Non-existing command
245 :: EXIT alone does not change the ERRORLEVEL
246 echo nonexisting ^& exit /b> tmp.cmd
249 call :checkErrorLevel 9009
251 echo nonexisting ^& exit /b> tmp.cmd
254 call :checkErrorLevel 9009
256 :: These tests show that || converts the returned error code
257 :: from RMDIR on failure, and converts it to an ERRORLEVEL
258 :: (first two tests: no ||, thus no ERRORLEVEL set;
259 :: last two tests: ||used and ERRORLEVEL is set).
262 echo --- CALL Batch RMDIR (no ERRORLEVEL set)
264 :: This test shows that if a batch returns error code 0 from CALL,
265 :: then CALL will keep the existing ERRORLEVEL (here, 111)...
266 echo rmdir nonexisting> tmp.cmd
267 echo exit /b>> tmp.cmd
270 call :checkErrorLevel 0
272 echo rmdir nonexisting> tmp.cmd
273 echo exit /b>> tmp.cmd
276 call :checkErrorLevel 111
278 echo --- CALL Batch RMDIR with ^|^| (sets ERRORLEVEL)
280 :: ... but if a non-zero error code is returned from CALL,
281 :: then CALL uses it as the new ERRORLEVEL.
282 echo rmdir nonexisting ^|^| rem> tmp.cmd
283 echo exit /b>> tmp.cmd
286 call :checkErrorLevel 2
287 :: This gives the same effect, since the last command's error code
288 :: is returned and transformed by CALL into an ERRORLEVEL:
289 echo rmdir nonexisting> tmp.cmd
292 call :checkErrorLevel 2
294 echo rmdir nonexisting ^|^| rem> tmp.cmd
295 echo exit /b>> tmp.cmd
298 call :checkErrorLevel 2
299 :: This gives the same effect, since the last command's error code
300 :: is returned and transformed by CALL into an ERRORLEVEL:
301 echo rmdir nonexisting> tmp.cmd
304 call :checkErrorLevel 2
307 :: Failing command (sets ERRORLEVEL to 1) and EXIT
308 echo --- CALL Batch DIR (sets ERRORLEVEL) - With failure
310 echo dir nonexisting^>NUL> tmp.cmd
313 call :checkErrorLevel 1
315 echo dir nonexisting^>NUL> tmp.cmd
318 call :checkErrorLevel 1
320 echo dir nonexisting^>NUL ^& goto :eof> tmp.cmd
323 call :checkErrorLevel 1
325 echo dir nonexisting^>NUL ^& goto :eof> tmp.cmd
328 call :checkErrorLevel 1
330 echo dir nonexisting^>NUL ^& exit /b> tmp.cmd
333 call :checkErrorLevel 1
335 echo dir nonexisting^>NUL ^& exit /b> tmp.cmd
338 call :checkErrorLevel 1
340 echo dir nonexisting^>NUL ^& exit /b 42 > tmp.cmd
343 call :checkErrorLevel 42
345 echo dir nonexisting^>NUL ^& exit /b 42 > tmp.cmd
348 call :checkErrorLevel 42
350 :: Succeeding command (sets ERRORLEVEL to 0) and EXIT
351 echo --- CALL Batch DIR (sets ERRORLEVEL) - With success
353 echo dir^>NUL> tmp.cmd
356 call :checkErrorLevel 0
358 echo dir^>NUL> tmp.cmd
361 call :checkErrorLevel 0
363 echo dir^>NUL ^& goto :eof> tmp.cmd
366 call :checkErrorLevel 0
368 echo dir^>NUL ^& goto :eof> tmp.cmd
371 call :checkErrorLevel 0
373 echo dir^>NUL ^& exit /b> tmp.cmd
376 call :checkErrorLevel 0
378 echo dir^>NUL ^& exit /b> tmp.cmd
381 call :checkErrorLevel 0
383 echo dir^>NUL ^& exit /b 42 > tmp.cmd
386 call :checkErrorLevel 42
388 echo dir^>NUL ^& exit /b 42 > tmp.cmd
391 call :checkErrorLevel 42
396 cd .. & rmdir /s/q foobar
400 :: ERRORLEVEL tests for special commands.
402 :: For some commands, the errorlevel is set differently,
403 :: whether or not they are run within a .BAT, a .CMD, or
404 :: directly from the command-line.
406 :: These commands are:
407 :: APPEND/DPATH, ASSOC, FTYPE, PATH, PROMPT, SET.
409 :: See https://ss64.com/nt/errorlevel.html for more details.
412 echo ---------- Testing ERRORLEVEL in .BAT and .CMD ----------
414 :: Use an auxiliary CMD file
415 mkdir foobar && cd foobar
417 echo --- In .BAT file
418 call :outputErrLvlTestToBatch tmp.bat
422 echo --- In .CMD file
423 call :outputErrLvlTestToBatch tmp.cmd
428 cd .. & rmdir /s/q foobar
430 :: Go to the next tests below.
433 :: ERRORLEVEL test helper function
434 :outputErrLvlTestToBatch (filename)
437 echo setlocal enableextensions>> %1
439 :: Reset the errorlevel
440 echo call :zeroErrLvl>> %1
450 echo echo %%errorlevel%%>> %1
451 echo assoc .nonexisting^>NUL>> %1
452 echo echo %%errorlevel%%>> %1
453 echo assoc .nonexisting^=^>NUL>> %1
454 echo echo %%errorlevel%%>> %1
464 echo echo %%errorlevel%%>> %1
465 echo path^;^>NUL>> %1
466 echo echo %%errorlevel%%>> %1
468 echo prompt^>NUL>> %1
469 echo echo %%errorlevel%%>> %1
470 echo prompt ^$p^$g^>NUL>> %1
471 echo echo %%errorlevel%%>> %1
472 echo prompt foobar^>NUL>> %1
473 echo echo %%errorlevel%%>> %1
476 echo echo %%errorlevel%%>> %1
477 echo set nonexisting^>NUL>> %1
478 echo echo %%errorlevel%%>> %1
479 echo set nonexisting^=^>NUL>> %1
480 echo echo %%errorlevel%%>> %1
481 echo set nonexisting^=trololol^>NUL>> %1
482 echo echo %%errorlevel%%>> %1
483 echo set nonexisting^=^>NUL>> %1
484 echo echo %%errorlevel%%>> %1
489 echo :zeroErrLvl>> %1
496 :: Next suite of tests.
501 :: Testing different ERRORLEVELs from the SET command.
502 :: See https://ss64.com/nt/set.html for more details.
504 echo ---------- Testing SET /A ERRORLEVELs ----------
509 call :checkErrorLevel 0
513 echo --- Unbalanced parentheses
516 call :checkErrorLevel 1073750988
520 echo --- Missing operand
523 call :checkErrorLevel 1073750989
527 echo --- Syntax error
530 call :checkErrorLevel 1073750990
534 echo --- Invalid number
536 set /a "total=0xdeadbeeg"
537 call :checkErrorLevel 1073750991
541 echo --- Number larger than 32-bits
543 set /a "total=999999999999999999999999"
544 call :checkErrorLevel 1073750992
548 echo --- Division by zero
551 call :checkErrorLevel 1073750993
560 echo --------- Finished --------------
564 if %errorlevel% neq %1 (echo Unexpected errorlevel %errorlevel%, expected %1) else echo OK
567 :: Subroutine to set errorlevel and return
568 :: in windows nt 4.0, this always sets errorlevel 1, since /b isn't supported
571 :: This line runs under cmd in windows NT 4, but not in more modern versions.