Batch file is a double-edged sword, the good side is that batch file runs on almost all of the Microsoft platforms, while the evil side is people just couldn't get it right.

At the highest level, batch file is interpreted by the command processor, which is cmd.exe or command.com. The interpreter is in charge of:

  • Process escape sequences (e.g. caret).
  • Expand environment variables.
  • Split the command string into parts.
  • Determine if the command is an internal command (e.g. ECHO), alias (which is specified using DOSKEY or AddConsoleAlias), or external command (which is controlled by PATH and PATHEXT).
  • Maintain command process and return value.
  • Take care of code page, pipe and I/O redirection.

What the command processor does not care are the C/C++ style "argv" parsing and double quotes - it is the responsibility of individual process to parse the command line, although in normal case this is handled by CRT or shell32!CommandLineToArgvW.

Let's jump to today's question - how do you write a batch file that prints out the full path of itself? (hint: %~dpnx0 and %~f0 have bug and won't work if the batch is invoked with double quotes).

 

I've put my answer as follows:

@ECHO OFF

SETLOCAL
SETLOCAL ENABLEEXTENSIONS
SETLOCAL ENABLEDELAYEDEXPANSION

IF EXIST "%~f0" (
  IF NOT "%~x0" == "" (
    GOTO ENTRYPOINT
  )
)

SET PATH_CDR=%CD%;%PATH%
:FINDNEXT
FOR /F "delims=; tokens=1,*" %%P IN ("!PATH_CDR!") DO (
  SET PATH_CAR=%%P
  IF "!PATH_CAR:~-1!" == "\" (
    SET PATH_CAR=!PATH_CAR:~0,-1!
  )
  IF NOT "!PATH_CAR!" == "" (
    FOR %%X IN (%PATHEXT:;=;%) DO (
      IF EXIST "!PATH_CAR!\%~n0%%X" (
        SET PATH_CAR=!PATH_CAR!\%~n0%%X
        GOTO TRAMPOLINE
      )
    )
  )
  SET PATH_CDR=%%Q
  IF DEFINED PATH_CDR (
    GOTO FINDNEXT
  )
)
ECHO Error: failed to detect batch file path 1>&2
EXIT /B -1

:TRAMPOLINE
CALL "!PATH_CAR!" %*
EXIT /B !ERRORLEVEL!

:ENTRYPOINT
SETLOCAL DISABLEDELAYEDEXPANSION
ECHO %~f0

EXIT /B %ERRORLEVEL%