Question

I have a which.bat on Windows 7,

@echo off
REM This bat searches a file in PATH list to see whether a file can be found.
REM If found, it shows the file's full path.
REM     which.bat gcc.exe
REM shows
REM     gcc.exe is found: D:\GMU\MinGW2\bin\gcc.exe
REM 
REM Note: Filename extension is significant in the search. E.g. If you run
REM     which.bat gcc
REM gcc.exe will not be matched.

IF "%1" == "" goto END

IF "%~$PATH:1" == "" (
      echo %1 is not found in any directories from PATH env-var.
    ) ELSE (
      echo %1 is found: %~$PATH:1
    )

:END

This bat works well until I find a strange behavior today.

There is a file O:\temp\pfiles (x86)\mystuff.txt, and PATH has content:

PATH=O:\temp\pfiles (x86);D:\CmdUtils

Running which mystuff.txt, I got the VERY STRANGE output:

\mystuff.txt was unexpected at this time.

enter image description here

After some poking around, I find that the (x86) in directory name causes the problem. To workaround, I have to add quotes to the echo, like this:

echo %1 is found: "%~$PATH:1"

The downside of such tweak is obvious: The quotes are printed to screen which is not always desired in the programmer's opinion.

Can anyone help explain this strange behavior?

I find this problem because in my real env, I have some paths like C:\Program Files (x86)\Common Files\NetSarang in PATH, which exhibit exactly the same symptom.

enter image description here

Was it helpful?

Solution

MS Dos is pretty simple shell implementation, and as I have figured out that interpretation of one DOS command line goes in 2 phases:

  1. evaluation of variables in current line
  2. interpretation of evaluated command line

In this case your command line:

IF "%~$PATH:1" == "" (
      echo %1 is not found in any directories from PATH env-var.
    ) ELSE (
      echo %1 is found: %~$PATH:1
    )

would be interpreted as:

IF "O:\temp\pfiles (x86)\mystuff.txt" == "" (
      echo mystuff is not found in any directories from PATH env-var.
    ) ELSE (
      echo mystuff.txt is found: O:\temp\pfiles (x86)\mystuff.txt
    )

Now we can notice the problem in (x86), i.e. interpreter sees this somehow like this - first ) closes else statement:

) ELSE (
      echo mystuff.txt is found: O:\temp\pfiles (x86
)\mystuff.txt
)

Solution: put "" around all potentially problematic variables.

I usually put quotes around the whole echo command content, for example:

echo "%1 is found: %~$PATH:1"

OTHER TIPS

As the problem is clear now (from Michael Burr and Robert Lujo), I try to show a solution.

You need quotes, but you don't want to display them.

With delayed expansion the closing parenthesis is harmless

setlocal EnableDelayedExpansion
IF "%~$PATH:1" == "" (
      echo %1 is not found in any directories from PATH env-var.
    ) ELSE (
      set "found=%~$PATH:1"      
      echo %1 is found: !found!
    )

Or just with a disappearing quote

IF "%~$PATH:1" == "" (
      echo %1 is not found in any directories from PATH env-var.
    ) ELSE (
      for %%^" in ("") do (
        echo %1 is found: %%~"%~$PATH:1
      )
    )

I can guess at an explanation (though not a helpful one): cmd.exe's parser isn't very clever - it gets confused by the parens in %~$PATH:1 - when it expands the variable and sees the ) character, it assumes that it's the closig paren for the ) ELSE ( line. (I think it doesn't do anything with the ( character in the expansion because those are only significant at the start of a command).

You can work around the problem by making sure that the expansing which can contain a ')' is not inside a (...) command grouping, or that it's quoted (as you found). Since you don't want the quotes, the other workaround might look like:

@echo off
REM This bat searches a file in PATH list to see whether a file can be found.
REM If found, it shows the file's full path.
REM     which.bat gcc.exe
REM shows
REM     gcc.exe is found: D:\GMU\MinGW2\bin\gcc.exe
REM 
REM Note: Filename extension is significant in the search. E.g. If you run
REM     which.bat gcc
REM gcc.exe will not be matched.

IF "%1" == "" goto END

IF "%~$PATH:1" == "" (
      echo %1 is not found in any directories from PATH env-var.
    ) ELSE (
      call :printfound %1
    )

goto END

:printfound
echo %1 is found: %~$PATH:1
goto :eof

:END

It's ugly, but that's the kind of thing you have to do with cmd.exe scripting.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top