Free Web Hosting Provider - Web Hosting - E-commerce - High Speed Internet - Free Web Page
Search the Web


Finding the location of a batch program                  laura fairhead
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                  22/08/01

There are many cases where it would be useful for
a program to locate itself or the directory it
lies in. The quick way of doing this is relatively
painless, just putting this at the start;

@IF NOT EXIST %0 %0.BAT

Will allow the batch to locate itself, but only
assuming that it is run from the current directory
or with a specified path.

The order which DOS goes through when a batch is
invoked from the command line;

  (i) If a path is specified the program is located
      from that path and only that path.
 (ii) DOS searches the current directory.
(iii) Each directory in the PATH variable is searched
      in turn.

Another thing DOS does is that if the .BAT extension
is not specified it will look for all runnable programs
(it checks for .COM then .EXE then .BAT).

To do this within a batch program itself it simply
has to follow the same (well, similar) steps as DOS
does, however it already knows that it isn't an EXE
or a COM program so things could be greatly simplified
at the beginning by adding a .BAT to the program name
(in %0) if there isn't already one there.

A normal way to do this is such;

ECHO %0*|FIND /I ".BAT*">NUL
IF ERRORLEVEL 1 %0.BAT

This works fine and is useful for most purposes however
in some cases it might be desireable to write a script
that doesn't use any pipes (because they require writeable
storage, in TEMP, which is not always available).

One way of doing it without using pipes is to utilise
a sort of caveat of DOS. This is basically due to the
effect of adding a single period to the filename. If
PROGRAM is a batch file;

       PROGRAM.BAT.       will run the batch file
       PROGRAM.           will NOT

So if the batch program tries to re-run itself with a period
appended to its name, the command will fail (and error printed)
if there was no extension. The command will suceed if there
was an extension, and in such case we know the original
filename should be left as it was;

IF !%1==!. %2 %3 %4
%0. . %0 . GOTO:K0
%0.BAT . GOTO:K0
:K0

This ensures there will be a BAT extension and the 'bad command
or filename' error we get (every time it is invoked without one)
can be cleaned up with CTTY NUL/CON.

The rest of the code is based around a loop, whereby directories
to search are in the parameter variables;

%0 . GOTO:K1 .\ %PATH%
:K1

Just sets the directory list to be the current directory '.\'
followed by consecutive directories in the PATH (remember that
the call regards the semicolons as deliminators);

parameter 0        1        2        3        4        5        6
          batch    .        GOTO:K1  .\       path1    path2    path3
          name
          (+BAT)

Because the loop is going to use SHIFT the batch name is saved
in %F%. This is going to be the file to look for in each directory.
The %E% variable is cleared initially, this is going to be the
directory in which to look and the loop is staggeredd such that
the first time around it will be nothing therefore allowing
programs to be found which have been invoked with a specified path.

SET F=%0
SET E=
GOTO L-

Adding a hyphen to a target labelname in a GOTO statement causes
the GOTO to actually 'skip' to the next line ( *[1]) it saves
having an extra label between SET/SHIFT lines;

:L
SET E=%2.\
SHIFT
IF !%1==! GOTO % stop hanging on X:filename %
IF NOT EXIST %E%%F% GOTO L

So each directory is checked in turn, but first with nothing
in %E% for files with paths already included. There is a safety
abort statement, so that if the parameters run out (and the program
would just loop forever) we can at least return control to the
user. You may prefer to add a target to the GOTO on that line
(to go to an error handler or something) rather than my emergency
STOP. The program _will_ actually cause this error abort in the cases
where the program has been invoked thus;

              drive:PROGRAM[.BAT]

If the program was not in the current directory of that drive
then it will fail, this is because DOS when given this form
of invocation will perform a PATH search (ie: it doesn't regard
this as specification of a path). Since this form of invocation
is extremely unlikely I think it is reasonable to let the program
simply fail in this case (and STOP, not hang). It may be an interesting
(and somewhat challenging) exercise for the reader to try to add
this behaviour into the program code.

Once the loop has done, the full program specification is in %E%%F%
and it is simply transferred to %0;

%E%%F% . GOTO:DON
:DON

Then working variables %E% and %F% are cleared and for the purposes
of this working demonstration program the filename is printed out.

FOR %%_ IN (E F) DO SET %%_=
ECHO FILENAME=%0


=====================================================================
Full listing                                      Tested:DOS6.22 DOS7
=====================================================================

@ECHO OFF
IF !%1==!. %2 %3 %4
CTTY NUL
%0. . %0 . GOTO:K0
%0.BAT . GOTO:K0
:K0
CTTY CON
%0 . GOTO:K1 .\ %PATH%
:K1
SET F=%0
SET E=
GOTO L-
:L
SET E=%2.\
SHIFT
IF !%1==! GOTO % stop hanging on X:filename %
IF NOT EXIST %E%%F% GOTO L
%E%%F% . GOTO:DON
:DON
FOR %%_ IN (E F) DO SET %%_=
ECHO FILENAME=%0


=====================================================================

[1] William Allen's 'skip'-jump goto technique. One of the many
    wonderful discoveries I have had the pleasure to witness him
    make in alt.msdos.batch. As usual, meticulously documented
    in a series of USENET postings, start here;

http://groups.google.com/groups?as_umsgid=9a7pp3$3t3mj$1@ID-55970.news.dfncis.de

    (owing to the nature of the pointy-clicky web this link is not
    guarenteed to stay around forever, so grab it now and print it out)