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.
The reason for this is that if you just start one bat file from another, only one of them will exit, while if using CALL, when the called bat file exits, the calling bat file will continue executing.
When used in a command line, script, or batch file, %1 is used to represent a variable or matched string. For example, in a Microsoft batch file, %1 can print what is entered after the batch file name.
By default, /F breaks up the command output at each blank space, and any blank lines are skipped.
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"
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With