Assorted Batch Tricks Sun 4-Sep-94 ===================== All rights reserved Copyright (c) 1993, 1994 by Timo Salmi .................................................................. Prof. Timo Salmi Co-moderator of comp.archives.msdos.announce Moderating at garbo.uwasa.fi anonymous FTP archives 128.214.87.1 Faculty of Accounting & Industrial Management; University of Vaasa Internet: ts@uwasa.fi BBS +(358)-61-3170972; FIN-65101, Finland .................................................................. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ This file belongs to TSBAT*.ZIP. Please do not distribute ³ ³ this batricks.txt file separately! ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Introduction ============ This file contains assorted batch tricks. Many, but not all, have been used in the TSBAT*.ZIP collection of batches. Likewise, there are some useful further tricks, not documented here, to be found in the TSBAT batches. Furthermore, many users have sent me useful suggestions and their own alternative solutions. My best thanks for the material. You can find much of this feedback stored in the garbo.uwasa.fi:/pc/pd2/tspost*.zip files. You are free to quote brief passages from this file provided you clearly indicate the source with a proper acknowledgment. Comments and corrections are solicited. But if you wish to have individual batch programming consultation, please rather post your question to a UseNet newsgroup like comp.os.msdos.programmer. It is much more efficient than asking me by email. I'd like to help, but I am very pressed for time. I prefer to pick the questions I answer from the Usenet news. Thus I can answer publicly at one go if I happen to have an answer. Besides, newsgroups have a number of readers who might know a better or an alternative answer. Don't be discouraged, though, if you get a reply like this from me. I am always glad to hear from fellow batch file users. INDEX ===== 1) Making "@echo off" general 2) Deleting all files 3) Nested loops 4) Checking whether a directory exists 5) Checking that a program is available at the current directory or at path 6) Using subroutines in batches 7) Convert a parameter to uppercase 8) Appending a new directory to the path 9) Comparing two files 10) Writing an empty line 11) Customizing the pause message 12) Complicate renaming with for 13) Checking for wildcards 14) Preventing breaking the batch 15) Prevent a break from bypassing your autoexec.bat 16) Getting the extension 17) The quote character % 18) Eliminating auxiliary batches 19) Utilizing the subst command in paths 20) How to run a batch once a week (testing for the weekday) 21) Testing if a file name includes a path 22) Showing the time without enter 23) Alternatives for testing for the errorlevel value 24) Redirecting a batch file's output 25) Testing for environment space sufficiency 26) A simple trick to "disable" a drive 27) Sending an escape sequence to the printer 28) Creating a random string 29) Finding out the length of a string 30) How to obtain the MsDos version into an environment variable 31) Finding out the number of regular files on a drive 32) Augmenting line numbers to a text file 33) Storing and returning to the original directory (push and pop) 34) Enticing the current date into an environment variable 35) Identifying the individual PC 36) For loop and redirection quirks 37) Traversing a directory tree 38) Echoing the redirection symbol 39) Getting the file basename 1. Making "@echo off" general ============================= If you want to turn the echo off, and do not wish to show that line on the screen, you can easily do this by applying @echo off There is a catch, however, because this only works since MsDos version 3.30. So if you want to make it general, put the following line in your autoexec.bat file if you are using MsDos 3.30 or higher set _echo=@ Then use the following format in your batches, which will then work for any MsDos version %_echo%echo off 2. Deleting all files ===================== One of the most Frequently Asked Questions (FAQs) about batches is how to suppress the "Are you sure (Y/N)?" confirmation requirement for del *.*. Use the following: echo y| del *.* If you wish to suppress the message too, use echo y| del *.* > nul Whether or not it is sensible to suppress the confirmation can be debated, but this is the trick anyway. 3. Nested loops =============== It is possible to have nested loops of a kind in batch programming. Consider the following two batches, and try it out by calling test.bat. echo off rem TEST.BAT for %%f in (a b c d e f) do %comspec% /c test2 %%f echo off rem TEST2.BAT for %%g in (1 2 3) do echo %1%%g Alternatively write everything below on a single line for %%f in (a b c d e f) do %comspec% /c for %%g in (1 2 3) do echo %%f%%g (The wrap has been used in the text is because of the right margin. Don't wrap your batch.). The disadvantage of this alternative is that the echo will be on. 4. Checking whether a directory exists ====================================== It is sometimes useful to be able to test whether a particular directory exists. The following test is true if the %1 directory does not exist. if not exist %1\nul if not exist %1nul echo Directory %1 does not exist 5. Checking that a program is available at the current directory or at path =========================================================================== When you call a program from a batch, and do not give the explicit path to it, it is advisable to test that the program is available either at the current directory or the default path. set _found= if exist %1 set _found=yes for %%d in (%path%) do if exist %%d\%1 set _found=yes for %%d in (%path%) do if exist %%d%1 set _found=yes if "%_found%"=="yes" goto _continue echo %1 is not at path or the current directory goto _out :_continue echo %1 found at path or in the current directory :_out 6. Using subroutines and recursion in batches ============================================= It is possible to use subroutines within batches. The crucial trick is setting an environment variable (eg _return) to point to a label where to return after the subroutine has been performed. For an example see UNPACK.BAT, and BOOT.BAT, the sections :_common and :_subru. Likewise it is possible to use recursion go emulate subroutines in batches. (Recursion means that a batch calls itself). As an example see SAFEDEL.BAT and trace the effects of the line for %%f in (%1) do call safedel %%f recurse Note that safedel could be replaced by %0 because the zeroeth parameter of a batch file points to itself. 7. Convert a parameter to uppercase =================================== This example shows how to ensure that the parameter %1 given to the batch is in uppercase. This utilizes the fact that MsDos converts the path to uppercase. The result is stored in upcase_ and then the original path is restored. set tmp_=%path% path=%1 set upcase_=%path% path=%tmp_% set tmp_= The also is another method for getting case-independent results. This is adapted from Jeff Prosise's column in PC Computing, March 1993, pp. 216-217. If the batch below is called TEST.BAT, it makes no difference whether you enter "TEST yes" or "TEST YES" or "TEST yEs". @echo off if not "%1"=="" set %1=***** set status_= if "%yes%"=="*****" set status_=yes if "%no%"=="*****" set status_=no if not "%status_%"=="" echo The parameter %%1 was a %status_% if "%status_%"=="" echo The parameter %%1 was neither a yes nor a no if not "%1"=="" set %1= 8. Appending a new directory to the path ======================================== This often needed trick is basically very simple. For example to add directory %1 to path use path=%path%;%1 Note that you can only use this trick in a batch. It will not work at the MsDos prompt because the environment variables are expanded (%path%) only within batches. For a full treatment with safeguards against appending non-existing directories, or appending twice, see ADDPATH.BAT. 9. Comparing two files ====================== It is possible in batch programming to test whether or not two files have identical contents. This trick utilizes th external MsDos programs fc.exe and find.exe. (An external MsDos program means, of course, a program that comes with the standard MsDos releases. Most often the MsDos external support files are located in a c:\dos directory.) fc %1 %2 > tmp$$$ type tmp$$$ | find /i "fc: no differences encountered" > diffe$$$ if exist notsame$ del notsame$ copy diffe$$$ notsame$ > nul if not exist notsame$ echo Files %1 and %2 are different if exist notsame$ echo Files %1 and %2 are identical if exist tmp$$$ del tmp$$$ if exist notsame$ del notsame$ if exist diffe$$$ del diffe$$$ If you think about, this idea can be used for other useful purposes, too, because it establishes whether a given string is found in a text file. 10. Writing an empty line ========================= This is a simple, but an often needed, useful trick. Just use echo with (for example) a point (.) after it. As you can see, I have utilized this batch feature extensively in my batch collection. echo. 11. Customizing the pause message ================================= You can easily customize the message given by pause by giving your own with echo and directing the pause message to nul. to nul. echo Break to quit, any other key to remove the tmp directory pause > nul 12. Complicate renaming with for ================================ Although this is basically trivial, one does not necessarily come to think of it. The for statement is quite useful for involved renaming of files. An example delineates. For example I have the following files (Turbo Pascal units for TP 4.0, 5.0, 5.5, 6.0 and 7.0). Say that I wish to rename them to be version 33 instead of 32. tspa3340.zip tspa3350.zip tspa3355.zip tspa3360.zip tspa3370.zip The following for-statement does that conveniently. for %f in (40 50 55 60 70) do ren tspa32%f.zip tspa33%f.zip Naturally, renaming is not the only task that can utilize this trick. I am sure you can readily think of others, like for %d in (a b) do format %d: 13. Checking for wildcards ========================== This example shows how you can test whether a parameter (%1) of a batch contains wildcards. @echo off for %%f in (%1) do if "%%f"=="%1" goto _nowilds echo Parameter %1 contains wildcards (or is missing) :_nowilds 14. Preventing breaking the batch ================================= It is possible to prevent the user from interrupting a batch by using the ctty command to reassign the input (and the output) device. Here is an example (an elementary password batch requiring inputting an e). Note the < and > redirections which are needed while the ctty has been assigned to nul. The ask batch enhancer is included in the TSBAT collection. @echo off ctty nul echo Now you cannot break the batch with ^C or ^Break > con :_ask echo Use e to break > con ask /b /d < con if errorlevel==101 if not errorlevel==102 goto _out goto _ask :_out ctty con echo Back to normal. Now you can break the batch with ^C or ^Break. Note that this trick does not prevent you from rebooting with alt-crtl-del while the batch is running. For that you need an external program like noboot.exe from garbo.uwasa.fi:/pc/ts/ tstsr17.zip (or whichever version number is current). 15. Prevent a break from bypassing your autoexec.bat ==================================================== You can actually prevent a quick tapping of the break from bypassing your autoexec.bat by a variation of the trick in the item above. Put for example shell=c:\command.com /p nul in your config.sys. Before you do, make sure to have a floppy to boot from in case something goes wrong. I first saw trick when it was posted in the UseNet comp.os.msdos.programmer newsgroup by Joseph Gil yogi@cs.ubc.ca. This is not, however, quite all there is to it. You should put ctty con as the last line to your autoexec.bat. If you don't, the keyboard will not be responding, and you must boot from the floppy you so sensibly had prepared :-). 16. Getting the extension ========================= It would be quite useful to be able to extract the extension of a given file name into an environment variable. Or to be able just to test whether there is an extension. Here is how to do that. The batch is based on the information in PC-Magazine July 1992, Vol 11, No. 13, page 528. It gives the crucial information that if one precedes the argument of a for loop with a slash (/), then the argument is interpreted in two parts. The first part is the first character of the argument, the second part all the rest. Neat, indeed. The problem with my solution below is that it will not recognize .* or .??? as extensions. But, of course, one can first test for wildcards as shown in a previous item "Checking for wildcards". See e.g. UNPACK.BAT for the utilization of this method. @echo off set exten_=%1 :_next set prev_=%exten_% for %%f in (/%exten_%) do set exten_=%%f if ".%exten_%"=="%prev_%" goto _extfound if not "%exten_%"=="%prev_%" goto _next goto _noext :_extfound echo The filename %1 has an extension %exten_% goto _out :_noext echo The filename %1 has no extension :_out set exten_= set prev_= 17. The quote character % ========================= As we know %1 indicates the first parameter given to a batch. Thus for example echo %1 echoes that parameter. But what if you want to echo the actual string %1 instead. The % character acts as a quote character. Thus echo %%1 will indeed be a "%1" instead of its usual interpretation. Try the following simple test @echo off if "%1"=="" goto _out echo %1 echo %%1 :_out See the item on "Eliminating auxiliary batches" for utilizing this feature. A good example of utilizing this feature is given by DELPATH.BAT. 18. Eliminating auxiliary batches ================================= Quite a number of batch programming tasks require an auxiliary batch which the primary batch has to call. Many of these cases can be eliminated by making the batch call itself (a kind of recursion). The auxiliary code is put in the batch itself. The trick is best illustrated by looking at the SHOW.BAT, which provides a wild-carded TYPE command, and would normally need an auxiliary file to type each of the individual files. Another example is given by the SAFEDEL.BAT batch. There is also an another trick for a similar purpose. The primary batch creates and auxiliary batch or batches, which it then calls. See DELPATH.BAT for an example of this method. Here is also a simple demonstration listing the drives on your system. (Only from c to t, actually because of the wrap I use here). @echo off echo @echo off> tmp$$$.bat echo if exist %%1:\nul echo Drive %%1: is present>> tmp$$$.bat for %%d in (c d e f g h i j k l m n o p q r s t) do call tmp$$$ %%d del tmp$$$.bat There was an inventive twist of this method in PC-Magazine August 1992, Vol. 11, No. 14, p. 527 for getting the volume label of a disk. Here is my own example using the same techniques. It sets the current directory in an environment variable getdir_. I have utilized this technique in PUSHDIRE.BAT. @echo off echo @echo off> director.bat echo set getdir_=%%2>> director.bat echo echo %%getdir_%%>> director.bat dir | find "Directory"> go.bat call go if exist director.bat del director.bat if exist go.bat del go.bat 19. Utilizing the subst command in paths ======================================== I use the following kind of a simple batch to make some of my directories easy to reach. The way this simple batch is written it avoids unnecessary errors if the substitution already has been made. As a last measure it shows the current substitution status. @echo off if exist m:\nul echo The substitution has already been made if not exist m:\nul subst m: c:\math if not exist s:\nul subst s: c:\support subst 20. How to run a batch once a week (testing for the weekday) ============================================================ The crucial trick is to be able to put the weekday into an environment variable. For the full treatment see WEEKLY.BAT. The essential trick needed is below, that is capturing the weekday into a weekday_ environment variable. No auxiliary programs outside the normal MsDos commands are needed. @echo off echo.| date | find "Current" > tmp$$$.bat echo set weekday_=%%3> current.bat call tmp$$$ echo %weekday_% if "%weekday_%"=="Fri" echo Thank God it's Friday if exist tmp$$$.bat del tmp$$$.bat if exist current.bat del current.bat set weekday_= In fact, if you substitute %%4 for the %%3 in the above, you'll capture today's date. Neat, eh? 21. Testing if a file name includes a path ========================================== First of all please see the earlier item "Getting the extension" because the same ideas are drawn upon. Testing whether the file name is a bare file name like go.exe or includes a path like r:\progs\go.exe is quite a complicated task if one wants to allow wildcarded names like r:\progs\*.exe. This can be done, and here is how. If one can figure this one out, one can safely say that one has begun to understand batch files. @echo off echo @echo off> tmp$$$.bat echo set rest_=%%1>> tmp$$$.bat echo :_next>> tmp$$$.bat echo set prev_=%%rest_%%>> tmp$$$.bat echo for %%%%g in (/%%rest_%%) do set rest_=%%%%g>> tmp$$$.bat echo if ":%%rest_%%"=="%%prev_%%" goto _found>> tmp$$$.bat echo if "\%%rest_%%"=="%%prev_%%" goto _found>> tmp$$$.bat echo if not "%%rest_%%"=="%%prev_%%" goto _next>> tmp$$$.bat echo goto _nopath>> tmp$$$.bat echo :_found>> tmp$$$.bat echo set haspath_=yes>> tmp$$$.bat echo goto _out>> tmp$$$.bat echo :_nopath>> tmp$$$.bat echo set haspath_=no>> tmp$$$.bat echo :_out>> tmp$$$.bat echo set rest_=>> tmp$$$.bat echo set prev_=>> tmp$$$.bat for %%f in (%1) do call tmp$$$ %%f if "%haspath_%"=="yes" echo Filename %1 includes a path if "%haspath_%"=="no" echo Filename %1 does not include a path rem if exist tmp$$$.bat del tmp$$$.bat set haspath_= 22. Showing the time without enter ================================== A simple trick to show the current time: echo.| time | find /v "new" For capturing the time into an environment variable see LASTBOOT.BAT. 23. Alternatives for testing for the errorlevel value ===================================================== Many programs and some MsDos commands (like diskcomp, format and xcopy) return an errorlevel exit code on termination. Testing for the errorlevel is complicated by the cumulative nature of errorlevels. Thus if you wish to test if the errorlevel was (exactly) 2, you must use if errorlevel==2 if not errorlevel==3 echo Errorlevel 2 Another alternative is utilizing the for command: for %%e in (0 1 2 3 4 5 6 7) do if errorlevel==%%e set _errlev=%%e if "%_errlev%"=="2" echo Errorlevel 2 Alternatively, and more generally for %%e in (0 1 2 3 4 5 6 7) do if errorlevel==%%e set _errlev=%%e if "%_errlev%"=="2" echo Errorlevel %_errlev% A convenient trick in more complicated batches is using the goto command: for %%e in (0 1 2) do if errorlevel==%%e goto _label%%e goto _out :_label0 echo Errorlevel 0 :_label1 echo Errorlevel 1 :_label2 echo Errorlevel 2 :_out See BOOT.BAT for actual usage of this technique. 24. Redirecting a batch file's output ===================================== Output from within a batch file is easily redirected. Consider a batchfile example.bat with the following contents @echo This is a redirection test> test Running "example" will produce a file "test" with This is a redirection test The line has an eoln (end of line: ascii 13 + 10) at the end. Note that it often is advisable not to leave any blank in front of the > redirection operator. Redirecting the output that a batch produces, is more complicated. Consider a batchfile example2.bat with the following contents @echo This is another redirection test Running example2 > test will produce an empty "test" file, while the text is echoed on the standard output. To redirect the output, you need to drive the batch through the command interpreter command.com like this (provided that command.com is at path or in the current directory). command /c example2 > test This will redirect the text to the "test" file. There is another quirk of redirection in MsDos batch programming best demonstrated by an example: @echo off rem This line will create an empty tmp.$$ file > tmp.$$ :: This line will not create an empty tmp.$$$ file > tmp.$$$ rem This line will cause problems: Press :: This line will not cause problems: Press As explained in PC Magazine Vol 12, Number 9, November 9, 1993, the reason is that the :: is taken as a label and not processed while the rem basically is an MsDos command that will be processed. The processing will start from the redirection at the end. This is the the also reason why redirection and the MsDos FOR command will cause problems. (See the entry "For loop and redirection quirks".) 25. Testing for environment space sufficiency ============================================= If your batch utilizes environment variables there is a possibility that you run out of environment space. If you get an "Out of environment space" message the well-known trick to increase your environment space by using shell configuration in config.sys: Example: shell=c:\bin\command.com c:\bin /e:1024 /p A perhaps less-known trick is that you can test in advance if your batch will run out of environment space. Below is an example showing you how to test if you have an additional 32 bytes of environment space still available for your batch: @echo off set test_=12345678901234567890123456789012 if "%test_%"=="12345678901234567890123456789012" goto _yes echo Insufficient environment space goto _out :_yes echo Sufficient environment space set test_= rem Whatever you wish to do :_out 26. A simple trick to "disable" a drive ======================================= It you wish temporarily disable a drive use the subst command for example as follows @echo off md c:\none subst d: c:\none To enable it again use @echo off subst d: /d rd c:\none 27. Sending an escape sequence to the printer ============================================= Here is a truly trivial trick. You cannot send escape sequences to the printer directory from the command line, but it is quite easy to do that from a simple batch file: @echo ESC%1> prn where you have to replace the ESC by the true escape character using your preferred editor. One snag with this methods is that it imposes a linefeed. 28. Creating a ramdom string ============================ I was asked on the UseNet news how to create a ramdom string. My reply. Please study the following example and expand on it @echo off echo 10 randomize(val(mid$(time$,7,2))) > tmp.bas echo 20 open "tmp2.bat" for output as #1 >> tmp.bas echo 30 x$ = mid$(str$(int(rnd*10000)),2) >> tmp.bas echo 40 print #1,"@set ramdom_=";x$ >> tmp.bas echo 50 close #2 >> tmp.bas echo 60 system >> tmp.bas gwbasic tmp.bas call tmp2 del tmp.bas del tmp2.bat set 29. Finding out the length of a string ====================================== The task of finding out the length of a string was tackled in PC Magazine January 26, 1993 issue. The solution is my own and more general, but naturally it has similar ingredients to the PC Magazine's. @echo off set test_=Testing the length of a string echo %test_% > len$&$&$ dir len$&$&$ | find "LEN$&$&$" > go$$$.bat echo @echo off> len$&$&$.bat echo set length_=%%1>> len$&$&$.bat call go$$$ echo The length is %length_% bytes del len$&$&$ del len$&$&$.bat del go$$$.bat 30. How to obtain the MsDos version into an environment variable ================================================================ Here is the code how to do it. @echo off ver > go$$$.bat echo @echo off> ms-dos.bat echo set version_=%%2>> ms-dos.bat call go$$$ echo Your MsDos version is %version_% del go$$$.bat del ms-dos.bat MsDos 5.0 version introduced many enhancements (like the loadhigh command, etc) and additions to the command switches (like /B and /S to the DIR command). Therefore it is useful to be able to test whether the batch is being run on a system taht is at least MsDos 5.0. Below is one option. rem Establish whether MsDos version 5.0 or later is being used set isver50_= ver | find "5.0" > tmpfind.$$$ ver | find "6.0" >> tmpfind.$$$ ver | find "6.2" >> tmpfind.$$$ copy tmpfind.$$$ tmpfind1.$$$ > nul del tmpfind.$$$ if exist tmpfind1.$$$ set isver50_=yes if exist tmpfind1.$$$ del tmpfind1.$$$ 31. Finding out the number of regular files on a drive ====================================================== Try attrib /s c:\*.* | find /c "\" The directories will not be (mis)counted as files as would with the dir command. Besides the dir command is not recursive until MsDos version 5.0. Note that if you do this for the same drive where you reside, you'll get one too many in the count because of the "|" pipe. 32. Augmenting line numbers to a text file ========================================== Occasionally it might be useful to put line number to a text file. Here is an example how to do it with MsDos commands only type YourFile.txt | find /v /n "&$&$&$123" > YourNew.txt The parameter &$&$&$123 stands for an improbable string, since find /v means displaying all the lines not containing it. 33. Storing and returning to the original directory (push and pop) ================================================================= There are several methods for (non-resident) pushing and popping the directory by bacth file techniques. In other words storing the current directory, changing the directory in between, and then returning to the starting directory. PUSHDIRE.BAT and POPDIRE.BAT give one method where the current drive and directory are stored in environment variables. The second method, displayed below, is a direct adaptation from Jeff Prosise's column in PC Computing, March 1993, pp. 216-217. Later the trick was presented again in Pc Magazine June 14, 1994, Vol. 13, No. 11, p. 357. The method is a very clever utilization of the prompt system. An example illustrates. @echo off echo @prompt cd $p$_$n:> r:\setback.bat %comspec% /c r:\setback> r:\goback.bat :: rem Change the drive and directory c: cd \dos echo The current directory is cd rem Do whatever you wish to do there pause :: rem Go back to the original drive and directory call r:\goback echo Now back in the original directory :: rem cleanup if exist r:\setback.bat del r:\setback.bat if exist r:\goback.bat del r:\goback.bat 34. Enticing the current date into an environment variable ========================================================== Like in the item "Storing and returning to the original directory" there are more than one way of doing this. One method is indicated in the item "How to run a batch once a week". The other (again) utilizes the prompt: @echo off echo @prompt set date_=$d> r:\tmp$$$.bat %comspec% /c r:\tmp$$$> r:\tmp2$$$.bat call r:\tmp2$$$ echo %date_% del r:\tmp$$$.bat del r:\tmp2$$$.bat If you look at your MsDos manual for the prompt special $ codes (like $d) that you can use in the prompt, you'll see that this method opens quite a number of possibilities of putting information into environment variables. Execise: Put the current weekday into an environment variable. Hint: Apply $d and $h. 35. Identifying the individual PC ================================= In cases of some batches it is useful to identify the PC the batch is run on. For example I use several different PCs myself and occasionally I need to differentiate between them. The solution is really trivial. Set an environment variable in the autoexec.bat to designate the PC. I use a variable pcid_ for this purpose. An outline batch illustrates. @echo off if "%pcid_%"=="" goto _none goto %pcid_% :dell echo Dell 325N laptop, do whatever goto _out :trifu echo Trifunic 386 desktop, do whatever goto _out :karvi echo "Garfunkel" Pinus 486 desktop, do whatever goto _out :_none echo PC not identified, do whatever :_out For example in the autoexec.bat of my DELL 325N laptop I have set pcid_=dell 36. For loop and redirection quirks =================================== A question from Newsgroups: comp.os.msdos.misc,comp.os.msdos.programmer > I am using DOS 5.0 and I have the following line in my batch file: > for %%f in (a b c d) do if exist %%f echo put %%f >> tmpfile > where a,b,c,d are some filenames. > > What I expect it to do is to echo the lines > put a > put b > put c > put d > into the file tmpfile. > > But what happen is after the "put a" is written to tmpfile, the rest of the > lines will just echo to the screen, look like that the redirection is not > working. > > If I take away the "if exist" everything is working fine. I found out > every time when I use a conditional statement with redirection, it will > only redirect the first time, the rest will echo to the screen. > > Is it the for loop cannot be mixed with the conditional statement and the > redirection? Yes, it can be mixed, but not so simply in this case. Use the following batch @echo off del tmpfile for %%f in (a b c d) do if exist %%f call auxil %%f where auxil.bat contains @echo off echo put %1>> tmpfile I'll be darned if I know why :-). In fact it is possible to do this with a single batch by employing the following method described in an earlier item @echo off echo @prompt echo put %%%%1$g$g tmpfile> tmp$$$.bat %comspec% /c tmp$$$> auxil.bat if exist tmpfile del tmpfile for %%f in (a b c d) do if exist %%f call auxil %%f del tmp$$$.bat del auxil.bat 37. Traversing a directory tree =============================== Traversing it straight up is relatively easy as can be seen from this example. @echo off :_loop dir/w if not exist ..\nul goto _out cd .. goto _loop :_out Going recursively down through a directory and its subdirectories is very complicated. Yet it can be done if you have MsDos 5.00 or beyond. The SWEEP.BAT bacth accompanying tsbat*.zip demonstrates how. The method is, however, too difficult to be of real practical importance. It is getter to use an auxiliary program for sweeping, like garbo.uwasa.fi:/pc/filefind/target15.zip or SWEEP.COM from /pc/pcmagvol/vol4n24.zip. 38. Echoing the redirection symbol ================================== In certain situations would be useful to be able echo the redirection symbol rather than have its perform its redirection function. For example your batch file might have a help line like this echo The line to customize is "echo dir/w %%%%3\%%2 >> %%new_%%" As you see the double quotes pre-empt the redirection. If you left them out, the line would result create a file %NEW_% containing "The line to customize is echo dir/w %%3\%2". Contrary to Unix, \ cannot be used to cover the special meaning of a symbol. As explained in the item "The quote character %" the % sign, can as is demonstrated above by the %% pairs. But %> does not take precedence over the redirection. 39. Getting the file basename ============================= Occasionally one needs to get the file name without the extension. Just like getting the extension from a file name using the "for %%f in (/%exten_%)" trick, even this can be done with batch commands only. The batch code for getting the basename has been presented by Neil Rubenking in PC Magazine April 26, 1994, Vol. 13, No. 8. But enough is enough even with batch tricks. The logic is getting overly complicated. One has to draw the line somewhere, stop kidding oneself, and start using batch enhancers (extenal programs to help out). I think here the limit has been reached. Hence I have included "basename" and "basepath" programs, which you can use to create the enhancers. They return the relevant information into an environment variable with that name. When you come to think of it. From one viewpoint, what else than batch enhancers are all the external MsDos commands (usually) in your C:\DOS directory? Using basename and basepath is very easy. Below is an example @echo off basename r:\cmand\command.com basepath r:\cmand\command.com echo %basename% echo %basepath% You can discard the environment variable simply by applying (note the two alternatives): set basename= basepath There is also a "basexten" batch enhancer in the /pc/ts/tsbat*.zip collection. Literature ========== Most books on batch programming, which I have seen, are too elementary to be really useful to the readers of this file. Hence this list is very brief indeed. Jamsa, Kris (1993). Concise Guide to MS-DOS Batch Files. Microsoft Press. (Draws heavily on DEBUG, but might be of general interest to you.) Some issues of magazines like the PC Magazine and PC Computing have contained much useful MsDos lore. But they have gradually become so heavily Windows oriented that they are losing their interest and usefulness to an MsDos user.