Reputation: 3787
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.
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.
Upvotes: 6
Views: 2754
Reputation: 82337
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
)
)
Upvotes: 4
Reputation: 340316
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.
Upvotes: 2
Reputation: 16371
MS Dos is pretty simple shell implementation, and as I have figured out that interpretation of one DOS command line goes in 2 phases:
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"
Upvotes: 8