views:

286

answers:

2

Howdy, I'm writing a batch script that will run on a Windows XP machine. This script needs to be able to reliably validate that a directory, passed as a command-line parameter, does in fact exist.

Assuming my script is named script.bat, it needs to support the following as legal command-line parameters:

C:\> script.bat C:
C:\> script.bat C:\
C:\> script.bat ..\photos
C:\> script.bat C:\WINDOWS
C:\> script.bat "C:\Documents and Settings"
C:\> script.bat "C:\Documents and Settings\"
C:\> script.bat F:\music\data\
C:\> ...

Basically, it must be legal to give a drive letter, with or without a trailing backslash as well as the full, absolute or relative path name, with or without a trailing backslash (and with or without spaces in some of the directory name(s)). The name MUST be quoted, obviously, if it contains spaces so as to be interpreted as one parameter. However, quotes should be allowed on names where they're not otherwise required:

C:\> script.bat "F:\music\data\"

My first attempt looks like this:

@ECHO OFF
SETLOCAL EnableExtensions EnableDelayedExpansion
IF ErrorLevel 1 GOTO :NoExtensions
IF "%~1"=="" GOTO :NoDirectory

SET dir=%~1\NUL
ECHO You provided:  %1
ECHO Testing using: %dir%
IF NOT EXIST %dir% GOTO :BadDirectory
SET dir=%~f1\
ECHO The following is valid: %dir%

REM
REM Other script stuff here...
REM

GOTO :EOF
:NoExtensions
    ECHO This batch script requires extensions!
    GOTO :EOF
:NoDirectory
    ECHO You must provide a directory to validate!
    GOTO :EOF
:BadDirectory
    ECHO The directory you specified is invalid!

The main problem is that it doesn't work when a path with embedded spaces is specified:

C:\script.bat "C:\Documents and Settings"
...
'and' is not recognized as an internal or external command,
operable program or batch file.

So, I tried quoting the %dir% variable as follows:

IF NOT EXIST "%dir%" GOTO :BadDirectory

Quoting as above seems to work when using "IF NOT EXIST" to check for files directly, but it doesn't work at all when using the "dir\NUL" trick to check for directories.

Another glitch in the original version is that if %1 is C: then the %~f1 version of the variable always seems to yield my current working directory instead of C: or C:\ as would be more useful. I'm not really sure of the best way to work around this either.

Any help or comments will be greatly appreciated!

-- Signed: wish I were writing bash shell scripts, ruby, or anything else instead...

+1  A: 

I have found working solution for a batch file. It doesn't involve using IF EXIST at all, but rather abuses PUSHD. I fumbled around a little with trying to coax IF EXIST to work but wavered between 90 % failure and 50 % success. Yuck. Then I noticed that I could try actually going into that directory to check whether it exists. This is subject to pretty much the same limitations like checking for "dir\nul" to exist as you need permissions to look inside the directory. Both attempts fail with things like System Volume Information, for example. But that's not the issue here.

So CD and PUSHD are both pretty smart when it comes to figuring out directories and it works like a charm.

(Note: all code, including tests can be found in my SVN).

@echo off
setlocal enableextensions
rem this one works reliably
if "%~1"=="" goto NoDirectory

pushd %1 2>nul && popd || goto :BadDirectory

echo DOES EXIST

endlocal
goto :eof

:NoDirectory
echo NO DIR
exit /b 1

:BadDirectory
echo DOES NOT EXIST
exit /b 1

After I got all my test cases right it works pretty nicely:

SUMMARY
   Succeeded: 60
   Failed:    0
   -----------------
   Total:     60

and before you ask: Yes, I've done unit testing on a batch file here. Someone please kill me for it.

Another note: I have left out error checking after setlocal because you only ever need this if you intend to run the script on DOS or Windows 9x/ME. Since this can't possibly work I tend to always name my batch files .cmd instead of .bat. AMissico noted that as well, but for the wrong reasons. The benefit of .cmd as extension is that neither DOS nor WinDOS will try executing it. No need to include error handling, then :-)

In general, however, I think you're better off using VBScript for such tasks. The Windows Script Host is included in all versions of Windows since 98 (even Win 7 still has it :-)) and therefore it's a pertty reliably platform.

Joey
A: 

Change the exist test to use \* and not \NUL, then you can quote the path. As far as the c: problem goes, just using "%~1\*" should always work (Windows can handle c:\\* and c:\folder\\*)

You should also note that storing a path in any form other than "%~[flags]1" can cause problems if the path contains one or more of % ! _ ^ (space)

Anders