Created
March 28, 2013 21:41
-
-
Save darcyparker/5267063 to your computer and use it in GitHub Desktop.
Tools written in a scripting language like node.js, ruby, python, etc... often wrap the script in a batch file that uses %~dp0 to identify the root folder of the command being called. In most scenarios %~dp0 gives the expected result. But sometimes they give the wrong result and cause the tool/script to fail. For example, some applications (such…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@echo off | |
REM Using %~dp0 is common in batch files to identify the root folder of the batch file | |
REM But sometimes %~dp0 does not work as expected. | |
REM | |
REM This batch file illustrates where %~dp0 fails to return desired root folder where this | |
REM batch file is located. | |
REM | |
REM To setup the demonstration: | |
REM - save this batch file to a location such as d:\bin\GetThisCommandsDir | |
REM - Go to a different folder to execute the test cases | |
REM | |
REM Test Cases: | |
REM First example, where %~dp0 works: | |
REM d:\>GetThisCommandsDir | |
REM Command and Arguments: GetThisCommandsDir | |
REM Where is this command using %~dp0: d:\bin_win32\ | |
REM Where is this command using :whereiscommand subroutine/method: d:\bin_win32\GetThisCommandsDir.bat | |
REM | |
REM Second example, where %~dp0 breaks: | |
REM d:\>"GetThisCommandsDir" | |
REM Command and Arguments: "GetThisCommandsDir" | |
REM Where is this command using %~dp0: d:\ | |
REM Where is this command using :whereiscommand subroutine/method: d:\bin_win32\GetThisCommandsDir.bat | |
REM | |
REM Notice the difference in the way the command is specified in each test case. | |
REM - The first case does not wrap the command in double quotes | |
REM (which is the way most people would execute the command.) | |
REM - The second case wraps the command name in arguments and fails to give the correct location | |
REM using %~dp0 | |
REM | |
REM Is the second test case that important? | |
REM - A command's name rarely (never in my experience) has spaces in it, so why would anyone | |
REM ever take the time to wrap it in double quotes if it is in the current dir or %PATH% | |
REM - The only real scenario someone would wrap the command in double quotes is when they need | |
REM to type out the full path because it is not in a folder specified by the %PATH% | |
REM | |
REM So, when would a command's name be wrapped in double quotes? | |
REM - It's a common practice for other programs to execute batch files and pass arguments | |
REM - These other programs need to 'escape' and/or wrap arguments being passed to the command | |
REM - For example, consider an argument with spaces in it. In windows (cmd.exe), the argument | |
REM needs to be wrapped in double quotes so that cmd.exe can tell it is a single argument and | |
REM not multiple arguments. | |
REM - The problem is that sometimes these programs do not construct the command line that uses the | |
REM batch file correctly. | |
REM - Many programs escape all arguments including the 0th argument (the command name itself) even | |
REM if it is not necessary. | |
REM - In unix-y shells like bash, this is not a problem. But as this program shows, it matters | |
REM on Windows with cmd.exe | |
REM | |
REM Example cases where I have seen this problem: | |
REM Generally programs that use batch files that wrap scripts for other scripting languages | |
REM - example many (not all) tools written in node.js, ruby, python, perl, etc... | |
REM - For example tools written for node.js are typically installed with npm, and a batch file | |
REM wrapper is used to execute the script in node.js. | |
REM - These batch files created by npm depend on %~dp0 !!! | |
REM - So other programs that call these tools need to verify they are not wrapping the 0th argument | |
REM in double quotes when using windows cmd.exe | |
REM - If these other programs cannot be updated, then the batch file needs to be updated to be | |
REM more robust. This script proposes a workaround. | |
REM | |
setlocal | |
set DEBUGLOG=GetThisCommandsDir.log | |
echo Command and Arguments: %0 %* | |
echo Command and Arguments: %0 %*>> %DEBUGLOG% | |
echo Where is this command using %%~dp0: %~dp0 | |
echo Where is this command using %%~dp0: %~dp0>> %DEBUGLOG% | |
call :whereiscommand %~n0 | |
echo Where is this command using :whereiscommand subroutine/method: %_whereiscommand% | |
echo Where is this command using :whereiscommand subroutine/method: %_whereiscommand%>> %DEBUGLOG% | |
goto :end | |
REM whereiscommand Finds a command in path environment variable | |
REM First param is command name to search for | |
:whereiscommand | |
set _whereiscommand= | |
for /F "usebackq delims==" %%z IN (`where %1`) DO call :__whereiscommand %%z | |
goto :eof | |
REM __whereiscommand is meant to be an Internal command that only sets %_whereiscommand% if it is not set already | |
REM This way the first found in `where %1` takes precedence | |
REM Note: a nested if statement with env var evaluations inside a for block doesn't work, | |
REM because variables in block are expanded before the loop is executed. | |
REM Workaround is to call a subroutine so changes to environment variables are seen for each iteration | |
:__whereiscommand | |
if /I "x%_whereiscommand%" EQU "x" ( | |
set _whereiscommand=%1 | |
) | |
goto :eof | |
endlocal | |
:end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Mentioned in preservim/tagbar#133