Friday, February 3, 2012

Evaluate relative paths using DOS functions (call:MakeAbsolute)

Window 7, Windows XP
I often hear people harp on about linux shell scripting and how M$ DOS is no where near as powerful. Well that may be so, but DOS can still be really powerful in it's own right. I have found an awesome resource recentely; DosTips - The DOS Batch Guide. They have developed a set of DOS functions that make DOS a breeze and show how powerful DOS can be. They also have tutorials on writing your own DOS functions.

In my case, I needed a method of evaluating relative paths against a given absolute path. The MakeAbsolute function on the DosTips website really helped me out and is now incorporated into almost all of my DOS scripts.
But before using any of their functions, I seriously recommend that you go through their functions tutorials. You will need to know what needs to be included in your script and how to call the functions in general. I am not going to go into that, but will rather provide a few tips for the MakeAboslute function.

So, the MakeAbsolute function...
The function accepts two inputs:
  1. The name of the variable that holds the relative path that must be evaluated
  2. The base path to which the relative path should be applied. (optional, defaults to the current working directory)
This might seem fairly simple at first, but there are a few tips to bare in mind:
  1. The first parameter to the function is the name of the variable that holds the relative path. So don't try to use % signs with it.
    set relpath=subdir\
    call:MakeAbsolute relpath "C:\Users"
    Notice that the parameter is the name of the variable (i.e. relpath) and not the value (i.e. %relpath%). This is critical.
  2. Contrary to 1 above, the second parameter is the value of the base path. If you are using a variable for the base path, then you must use the % symbols for this parameter.
    set relpath=subdir\
    set basepath=C:\Users
    call:MakeAbsolute relpath "%basepath%"
  3. Do not use a variable name of "src" as the first input. This will conflict with the MakeAbsolute functions internal variable declaration. Wierd things start happening.
  4. When setting variables, do not use quotes with paths, not even when they have spaces in the path names.
    set wontwork="quoted path"
    set willwork=unquoted path
  5. I suggest that you always use quotes on the base path parameter (second parameter). If the variable used for the base path parameter has a value with spaces in it, then the function will not execute correctly if you do not include quotes around this value.
    set relpath=subdir\
    set basepath=C:\Program Files\
    call:MakeAbsolute relpath "%basepath%"
    Note that there are no quotes when setting the variable, but there must be quotes when using the variable.
  6. If you do not provide a second input, the current working directory is used as base path.
  7. Starting a relative path with a backslash or period or both (\ or . or .\) will all equate to the base path provided in the second parameter (or the current working directory if a base path has not been provided)
  8. Forward slashes are converted to backslashes automagically
  9. Last but quite conveniently; none of the paths provided to or returned from the function have to actually exist.
The following script provides some examples of supported relative paths:
@echo off

set scriptpath=%~dp0
set siblingfile=sibling.bat
set siblingfolder=sibling\
set fnwsfolder=folder name with spaces\
set descendantfolder=sibling\descendant\
set ancestorfolder=..\..\
set cousinfolder=..\uncle\cousin

call:MakeAbsolute siblingfile      "%scriptpath%"
call:MakeAbsolute siblingfolder    "%scriptpath%"
call:MakeAbsolute fnwsfolder       "%scriptpath%"
call:MakeAbsolute descendantfolder "%scriptpath%"
call:MakeAbsolute ancestorfolder   "%scriptpath%"
call:MakeAbsolute cousinfolder     "%scriptpath%"

echo scriptpath:       %scriptpath%
echo siblingfile:      %siblingfile%
echo siblingfolder:    %siblingfolder%
echo fnwsfolder:       %fnwsfolder%
echo descendantfolder: %descendantfolder%
echo ancestorfolder:   %ancestorfolder%
echo cousinfolder:     %cousinfolder%

:: Function declarations
:: Handy to read for how dos functions
:: work.
:MakeAbsolute file base -- makes a file name absolute considering a base path
::                      -- file [in,out] - variable with file name to be converted, or file name itself for result in stdout
::                      -- base [in,opt] - base path, leave blank for current directory
:$created 20060101 :$changed 20080219 :$categories Path
set "src=%~1"
if defined %1 set "src=!%~1!"
set "bas=%~2"
if not defined bas set "bas=%cd%"
for /f "tokens=*" %%a in ("%bas%.\%src%") do set "src=%%~fa"
    IF defined %1 (SET %~1=%src%) ELSE ECHO.%src%
This will produce output like the following:
C:\Users\dayneo\Documents\Visual Studio 2005\Projects\dosscript>myscript
scriptpath:       C:\Users\dayneo\Documents\Visual Studio 2005\Projects\dosscrip
siblingfile:      C:\Users\dayneo\Documents\Visual Studio 2005\Projects\dosscrip
siblingfolder:    C:\Users\dayneo\Documents\Visual Studio 2005\Projects\dosscrip
fnwsfolder:       C:\Users\dayneo\Documents\Visual Studio 2005\Projects\dosscrip
t\folder name with spaces\
descendantfolder: C:\Users\dayneo\Documents\Visual Studio 2005\Projects\dosscrip
ancestorfolder:   C:\Users\dayneo\Documents\Visual Studio 2005\
cousinfolder:     C:\Users\dayneo\Documents\Visual Studio 2005\Projects\uncle\co

C:\Users\dayneo\Documents\Visual Studio 2005\Projects\dosscript>


Irq Mishell said...

How can i resolve directory under C:/Program files (86) ?

Anonymous said...

You need to add delims= at the end of the for to process paths with spaces in them