Windows .bat文件,%~$PATH:1引号问题

6

我有一个在Windows 7上的which.bat文件,

@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

这个批处理文件一直很好用,但今天我发现了一个奇怪的问题。
有一个文件 O:\temp\pfiles (x86)\mystuff.txt,并且路径包含在PATH中:
PATH=O:\temp\pfiles (x86);D:\CmdUtils

运行which mystuff.txt命令,我得到了一个非常奇怪的输出结果:

\mystuff.txt was unexpected at this time.

enter image description here

经过一番探索,我发现目录名中的(x86)导致了问题。为了解决这个问题,我不得不在echo命令中添加引号,像这样:

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

这种调整的缺点显而易见:引号被打印到屏幕上,这在程序员看来并不总是理想的。有人能帮忙解释一下这种奇怪的行为吗?我发现这个问题是因为在我的真实环境中,我有一些路径,如 C:\Program Files (x86)\Common Files\NetSarang 在 PATH 中,出现了完全相同的症状。

谢谢您让我知道。where.exe非常好用。自从Windows XP时期还没有where.exe,我一直在使用which.bat - Jimm Chen
好的信息...但如果你正在搜索一个没有扩展名的'可执行文件',那么它不起作用...使用'where.exe'...或者如果你在Linux上使用'which'...或者只需安装Cygwin并使用'which'...效果很棒!!! - ZEE
3个回答

8

MS Dos是一个相当简单的Shell实现,我发现一个DOS命令行的解释有两个阶段:

  1. 在当前行中评估变量
  2. 解释已评估的命令行

在这种情况下,您的命令行:

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

将被解释为:

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
    )

现在我们可以注意到在 (x86) 中出现了问题,即解释器将其视为以下形式 - 首先)关闭else语句:
) ELSE (
      echo mystuff.txt is found: O:\temp\pfiles (x86
)\mystuff.txt
)

解决方案:在所有可能存在问题的变量周围加上引号。通常,我会在整个echo命令内容周围加上引号,例如:
echo "%1 is found: %~$PATH:1"

非常具體的解釋。 - Jimm Chen

4

现在问题已经清楚了(来自Michael Burr和Robert Lujo的意见),我尝试展示一个解决方案。

你需要引号,但你不想显示它们。

使用延迟扩展,闭括号是无害的。

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!
    )

或者只使用消失的引号。
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
      )
    )

EnableDelayedExpansion方法更加清晰易懂。谢谢。 - Jimm Chen
1
哇,"set "found=1"" 和 "set found=1" 是完全相同的,真是个巧妙的技巧。 - Jimm Chen

2
我可以猜测一个解释(虽然不是很有帮助):cmd.exe的解析器不够聪明——它会被%~$PATH:1中的括号所困惑——当它展开变量并看到)字符时,它会假定它是) ELSE (行的闭合括号。(我认为它在展开中不处理(字符,因为那些只有在命令开头才有意义)。
您可以通过确保扩展的内容不包含在(...)命令分组内或将其引用(如您发现的那样)来解决问题。由于您不想要引号,另一种解决方法可能如下所示:
@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

虽然不太美观,但这是你在cmd.exe脚本编写中必须要做的事情。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接