如何检查%PATH%中是否存在目录?

Axl*_*Axl 59 windows batch-file

如何检查PATH环境变量中是否已存在目录?这是一个开始.但是,我已经设法使用下面的代码,它回显了%PATH%中的第一个目录.由于这是一个FOR循环,你会认为它将枚举%PATH%中的所有目录,但它只获得第一个目录.

有没有更好的方法呢?像%PATH%变量上运行的find或findstr之类的东西?我只想检查%PATH%目录列表中是否存在目录,以避免添加可能已存在的内容.

FOR /F "delims=;" %%P IN ("%PATH%") DO (
    @ECHO %%~P
)
Run Code Online (Sandbox Code Playgroud)

dbe*_*ham 88

首先,我将指出一些使这个问题难以完美解决的问题.然后我将介绍我能够提出的最具防弹性的解决方案.

在本讨论中,我将使用小写路径表示文件系统中的单个文件夹路径,使用大写PATH表示PATH环境变量.

从实际角度来看,大多数人都想知道PATH是否包含给定路径的逻辑等价物,而不是PATH是否包含给定路径的精确字符串匹配.这可能有问题,因为:

  1. 尾部\在路径中是可选的
    大多数路径在有和没有尾部的情况下都能很好地工作\.路径逻辑上指向同一位置.PATH经常有路径的混合,有或没有尾随\.在搜索匹配的PATH时,这可能是最常见的实际问题.

    • 有一个例外:相对路径C:(表示驱动器C的当前工作目录)与C:\(意味着驱动器C的根目录 )非常不同

  2. 某些路径具有备用短名称
    任何不符合旧8.3标准的路径都具有符合标准的备用短格式.这是我在一些频率上看到的另一个PATH问题,特别是在商业环境中.

  3. Windows接受路径中的两个/\作为文件夹分隔符.
    这种情况不常见,但可以使用/而不是在路径中指定路径\,它将在PATH(以及许多其他Windows上下文)中正常运行

  4. Windows将连续文件夹分隔符视为一个逻辑分隔符.
    C:\ FOLDER \\和C:\ FOLDER \是等效的.在处理路径时,这实际上有助于许多上下文,因为开发人员通常可以附加\到路径而无需检查尾随是否\已存在.但是,如果尝试执行精确的字符串匹配,这显然会导致问题.

    • 例外:不仅C:比不同C:\,但C:\(一个有效的路径),比不同C:\\(一个无效的路径).

  5. Windows修剪文件和目录名称的尾随点和空格.
    "C:\test. "相当于"C:\test".

  6. 当前.\和父..\文件夹说明符可能出现在路径中
    不太可能在现实生活中看到,但类似的东西C:\.\parent\child\..\.\child\相当于C:\parent\child

  7. 路径可以选择用双引号括起来.
    路径通常用引号括起来以防止特殊字符,如<space> , ; ^ & =.实际上,在路径之前,之内和/或之后可以出现任意数量的引号.它们被Windows忽略,除非是为了防止特殊字符.除非路径包含a ;,否则在PATH中永远不需要引号,但引号可能永远不会出现.

  8. 路径可以是完全合格的或相对的.
    完全限定的路径指向文件系统中的一个特定位置.相对路径位置根据当前工作卷和目录的值而变化.相对路径有三种主要风格:

    • D: 相对于卷D的当前工作目录:
    • \myPath 是相对于当前的工作量(可能是C:,D:等)
    • myPath 是相对于当前工作卷和目录

    在PATH中包含相对路径是完全合法的.这在Unix世界中很常见,因为默认情况下Unix不搜索当前目录,因此Unix PATH通常会包含.\.但Windows默认搜索当前目录,因此Windows PATH中的相对路径很少见.

因此,为了可靠地检查PATH是否已包含路径,我们需要一种方法将任何给定路径转换为规范(标准)形式.~sFOR变量和参数扩展使用的修饰符是一个解决问题1 - 6的简单方法,部分解决了问题7. ~s修饰符删除了封闭引号,但保留了内部引号.通过在比较之前显式删除所有路径中的引号,可以完全解决问题7.请注意,如果路径实际上不存在,那么~s修饰符将不会附加\到路径,也不会将路径转换为有效的8.3格式.

问题~s是它将相对路径转换为完全限定的路径.这对问题8来说是有问题的,因为相对路径永远不应该与完全限定的路径匹配.我们可以使用FINDSTR正则表达式将路径分类为完全限定或相对.正常的完全限定路径必须以<letter>:<separator>但不包括<letter>:<separator><separator>,其中<separator>是\或者/.UNC路径始终是完全合格的,必须从头开始\\.比较完全限定路径时,我们使用~s修饰符.比较相对路径时,我们使用原始字符串.最后,我们从不将完全限定的路径与相对路径进行比较.该策略为问题8提供了一个很好的实用解决方案.唯一的限制是两个逻辑等效的相对路径可以被视为不匹配,但这是一个小问题,因为相对路径在Windows PATH中很少见.

还有一些其他问题使这个问题复杂化:

9)处理包含特殊字符的PATH时,正常扩展不可靠.
特殊字符不需要在PATH中引用,但它们可以是.因此,像一个路径 C:\THIS & THAT;"C:\& THE OTHER THING"是完全有效的,但不能使用简单的扩展,因为这两个安全扩展"%PATH%",并%PATH%会失败.

10)路径分隔符在路径名中也是有效的
.A ;用于分隔PATH中的路径,但;也可以是路径中的有效字符,在这种情况下必须引用路径.这会导致解析问题.

jeb解决了问题9和10在'漂亮的打印'窗口%PATH%变量 - 如何拆分';' 在CMD shell中

因此,我们可以将~s修改器和路径分类技术与我对jeb的PATH解析器的变体结合起来,以获得这种近乎防弹的解决方案,用于检查PATH中是否已存在给定路径.该函数可以在批处理文件中包含和调用,也可以单独使用,并作为自己的inPath.bat批处理文件调用.它看起来像很多代码,但超过一半是评论.

@echo off
:inPath pathVar
::
::  Tests if the path stored within variable pathVar exists within PATH.
::
::  The result is returned as the ERRORLEVEL:
::    0 if the pathVar path is found in PATH.
::    1 if the pathVar path is not found in PATH.
::    2 if pathVar is missing or undefined or if PATH is undefined.
::
::  If the pathVar path is fully qualified, then it is logically compared
::  to each fully qualified path within PATH. The path strings don't have
::  to match exactly, they just need to be logically equivalent.
::
::  If the pathVar path is relative, then it is strictly compared to each
::  relative path within PATH. Case differences and double quotes are
::  ignored, but otherwise the path strings must match exactly.
::
::------------------------------------------------------------------------
::
:: Error checking
if "%~1"=="" exit /b 2
if not defined %~1 exit /b 2
if not defined path exit /b 2
::
:: Prepare to safely parse PATH into individual paths
setlocal DisableDelayedExpansion
set "var=%path:"=""%"
set "var=%var:^=^^%"
set "var=%var:&=^&%"
set "var=%var:|=^|%"
set "var=%var:<=^<%"
set "var=%var:>=^>%"
set "var=%var:;=^;^;%"
set var=%var:""="%
set "var=%var:"=""Q%"
set "var=%var:;;="S"S%"
set "var=%var:^;^;=;%"
set "var=%var:""="%"
setlocal EnableDelayedExpansion
set "var=!var:"Q=!"
set "var=!var:"S"S=";"!"
::
:: Remove quotes from pathVar and abort if it becomes empty
set "new=!%~1:"=!"
if not defined new exit /b 2
::
:: Determine if pathVar is fully qualified
echo("!new!"|findstr /i /r /c:^"^^\"[a-zA-Z]:[\\/][^\\/]" ^
                           /c:^"^^\"[\\][\\]" >nul ^
  && set "abs=1" || set "abs=0"
::
:: For each path in PATH, check if path is fully qualified and then do
:: proper comparison with pathVar.
:: Exit with ERRORLEVEL 0 if a match is found.
:: Delayed expansion must be disabled when expanding FOR variables
:: just in case the value contains !
for %%A in ("!new!\") do for %%B in ("!var!") do (
  if "!!"=="" endlocal
  for %%C in ("%%~B\") do (
    echo(%%B|findstr /i /r /c:^"^^\"[a-zA-Z]:[\\/][^\\/]" ^
                           /c:^"^^\"[\\][\\]" >nul ^
      && (if %abs%==1 if /i "%%~sA"=="%%~sC" exit /b 0) ^
      || (if %abs%==0 if /i "%%~A"=="%%~C" exit /b 0)
  )
)
:: No match was found so exit with ERRORLEVEL 1
exit /b 1
Run Code Online (Sandbox Code Playgroud)

该函数可以像这样使用(假设批处理文件名为inPath.bat):

set test=c:\mypath
call inPath test && (echo found) || (echo not found)
Run Code Online (Sandbox Code Playgroud)



通常,检查路径是否存在于PATH中的原因是因为如果路径不存在,则要追加路径.这通常只需使用类似的东西即可完成path %path%;%newPath%.但问题9证明了这不可靠.

另一个问题是如何在函数结束时返回ENDLOCAL屏障的最终PATH值,特别是如果可以在启用或禁用延迟扩展的情况下调用该函数.!如果启用延迟扩展,任何未转义将破坏该值.

使用jeb在这里发明的惊人的安全返回技术解决了这些问题:http://www.dostips.com/forum/viewtopic.php? p = 6930#p6930

@echo off
:addPath pathVar /B
::
::  Safely appends the path contained within variable pathVar to the end
::  of PATH if and only if the path does not already exist within PATH.
::
::  If the case insensitive /B option is specified, then the path is
::  inserted into the front (Beginning) of PATH instead.
::
::  If the pathVar path is fully qualified, then it is logically compared
::  to each fully qualified path within PATH. The path strings are
::  considered a match if they are logically equivalent.
::
::  If the pathVar path is relative, then it is strictly compared to each
::  relative path within PATH. Case differences and double quotes are
::  ignored, but otherwise the path strings must match exactly.
::
::  Before appending the pathVar path, all double quotes are stripped, and
::  then the path is enclosed in double quotes if and only if the path
::  contains at least one semicolon.
::
::  addPath aborts with ERRORLEVEL 2 if pathVar is missing or undefined
::  or if PATH is undefined.
::
::------------------------------------------------------------------------
::
:: Error checking
if "%~1"=="" exit /b 2
if not defined %~1 exit /b 2
if not defined path exit /b 2
::
:: Determine if function was called while delayed expansion was enabled
setlocal
set "NotDelayed=!"
::
:: Prepare to safely parse PATH into individual paths
setlocal DisableDelayedExpansion
set "var=%path:"=""%"
set "var=%var:^=^^%"
set "var=%var:&=^&%"
set "var=%var:|=^|%"
set "var=%var:<=^<%"
set "var=%var:>=^>%"
set "var=%var:;=^;^;%"
set var=%var:""="%
set "var=%var:"=""Q%"
set "var=%var:;;="S"S%"
set "var=%var:^;^;=;%"
set "var=%var:""="%"
setlocal EnableDelayedExpansion
set "var=!var:"Q=!"
set "var=!var:"S"S=";"!"
::
:: Remove quotes from pathVar and abort if it becomes empty
set "new=!%~1:"^=!"
if not defined new exit /b 2
::
:: Determine if pathVar is fully qualified
echo("!new!"|findstr /i /r /c:^"^^\"[a-zA-Z]:[\\/][^\\/]" ^
                           /c:^"^^\"[\\][\\]" >nul ^
  && set "abs=1" || set "abs=0"
::
:: For each path in PATH, check if path is fully qualified and then
:: do proper comparison with pathVar. Exit if a match is found.
:: Delayed expansion must be disabled when expanding FOR variables
:: just in case the value contains !
for %%A in ("!new!\") do for %%B in ("!var!") do (
  if "!!"=="" setlocal disableDelayedExpansion
  for %%C in ("%%~B\") do (
    echo(%%B|findstr /i /r /c:^"^^\"[a-zA-Z]:[\\/][^\\/]" ^
                           /c:^"^^\"[\\][\\]" >nul ^
      && (if %abs%==1 if /i "%%~sA"=="%%~sC" exit /b 0) ^
      || (if %abs%==0 if /i %%A==%%C exit /b 0)
  )
)
::
:: Build the modified PATH, enclosing the added path in quotes
:: only if it contains ;
setlocal enableDelayedExpansion
if "!new:;=!" neq "!new!" set new="!new!"
if /i "%~2"=="/B" (set "rtn=!new!;!path!") else set "rtn=!path!;!new!"
::
:: rtn now contains the modified PATH. We need to safely pass the
:: value accross the ENDLOCAL barrier
::
:: Make rtn safe for assignment using normal expansion by replacing
:: % and " with not yet defined FOR variables
set "rtn=!rtn:%%=%%A!"
set "rtn=!rtn:"=%%B!"
::
:: Escape ^ and ! if function was called while delayed expansion was enabled.
:: The trailing ! in the second assignment is critical and must not be removed.
if not defined NotDelayed set "rtn=!rtn:^=^^^^!"
if not defined NotDelayed set "rtn=%rtn:!=^^^!%" !
::
:: Pass the rtn value accross the ENDLOCAL barrier using FOR variables to
:: restore the % and " characters. Again the trailing ! is critical.
for /f "usebackq tokens=1,2" %%A in ('%%^ ^"') do (
  endlocal & endlocal & endlocal & endlocal & endlocal
  set "path=%rtn%" !
)
exit /b 0
Run Code Online (Sandbox Code Playgroud)

  • ++这是我见过的最完整的路径问题描述:-) (20认同)
  • 1)我的原帖有关于路径中连续点的虚假陈述.这被关于尾随点的正确问题所取代.2)最初忘记了短名称问题所以我使用了~f修饰符.对~s修饰符的改变是一个重大改进. (2认同)
  • 对代码进行了重大更改以支持相对路径,并允许在启用延迟扩展时调用函数.还添加了更多解释以及对代码的广泛注释. (2认同)
  • @mattwilkie - 不,这个例子是正确的.该脚本需要包含要测试的路径的变量的名称.它不期望字符串文字.它是一种通过批量引用传递值的方法. (2认同)
  • @dbenham 是的,这是我的错。我正在使用字符串文字。但由于其他人也有同样的问题,这意味着答案不是很像样。我知道已经完成了很多工作(和出色的工作),但是 99.9% 的访问此页面的人可能正在寻找像 `echo ;%PATH%; 这样简单的东西。| find /C /I ";&lt;string&gt;;"`(如@Andriy M 对兰迪回答的评论) (2认同)

Ran*_*ndy 38

我有一段时间没有做任何批处理文件编程,但是:

echo ;%PATH%; | find /C /I ";<string>;"
Run Code Online (Sandbox Code Playgroud)

如果找不到字符串,则应该为0,如果为,则为1或更多.

编辑:添加不区分大小写的标志,感谢Panos.

  • 实际上,这种优雅的单线可以很容易地修复:`echo;%PATH%; | find/C/I"; <string>;"`. (14认同)
  • 这真的解决了Axl的问题吗?他想检查是否添加一个目录,比如说C:\ foo\bar到PATH是多余的.假设他的PATH已经包含c:\ foo,你的支票会说"是的,它是多余的",而实际上并非如此. (2认同)
  • @JeffG:如果我的建议还不算太晚,您可以尝试将 `;%PATH%;` 括在引号中:`echo ";%PATH%;" | find /C /I ";&lt;string&gt;;"`。 (2认同)

ber*_*ghe 21

检查某些东西是否在路径中的另一种方法是执行一些无辜的可执行文件,如果它存在则不会失败,并检查结果.例如,下一个代码片段会检查maven是否在路径中:

mvn --help > NUL 2> NUL 
if errorlevel 1 goto mvnNotInPath
Run Code Online (Sandbox Code Playgroud)

所以我尝试运行mvn --help,忽略输出(如果maven在那里,实际上不想看到帮助)(> NUL),如果找不到maven,也不显示错误消息(2> NUL).

  • 聪明简单快捷 (2认同)

Aac*_*ini 8

在这里阅读的答案后,我想我可以提供一个新的观点:如果这个问题的目的是要知道如果路径有一定的可执行文件的存在%PATH%,如果没有,将其插入(和这是唯一的原因做我认为,那么它可能会以稍微不同的方式解决:"如何检查%PATH%中是否存在某个可执行程序的目录"?这个问题可以通过这种方式轻松解决:

for %%p in (programname.exe) do set "progpath=%%~$PATH:p"
if not defined progpath (
   rem The path to programname.exe don't exist in PATH variable, insert it:
   set "PATH=%PATH%;C:\path\to\progranname"
)
Run Code Online (Sandbox Code Playgroud)

如果您不知道可执行文件的扩展名,请查看所有文件:

set "progpath="
for %%e in (%PATHEXT%) do (
   if not defined progpath (
      for %%p in (programname.%%e) do set "progpath=%%~$PATH:p"
   )
)
Run Code Online (Sandbox Code Playgroud)

  • +1,如果您不知道文件夹中程序的名称,那么您只需在该位置创建一个虚拟文件(假设您具有写访问权限).它不需要PATHEXT扩展.记得在完成后删除虚拟文件. (2认同)

Ati*_*ziz 6

使用fordelims,你不能捕获任意数量的字段(正如亚当指出的那样),所以你必须使用循环技术.以下命令脚本将PATH在单独的行中列出环境变量中的每个路径:

@echo off 
setlocal 
if "%~1"=="" (
    set PATHQ=%PATH%
) else (
    set PATHQ=%~1 ) 
:WHILE
    if "%PATHQ%"=="" goto WEND
    for /F "delims=;" %%i in ("%PATHQ%") do echo %%i
    for /F "delims=; tokens=1,*" %%i in ("%PATHQ%") do set PATHQ=%%j
    goto WHILE 
:WEND
Run Code Online (Sandbox Code Playgroud)

它模拟了许多编程语言中的经典while ... wend构造.有了这个,你可以使用类似的东西findstr随后过滤并寻找特定的路径.例如,如果您将上述脚本保存在一个名为tidypath.cmdthen 的文件中,那么您可以在这里找到管道findstr,查找标准程序目录下的路径(使用不区分大小写的匹配):

> tidypath | findstr /i "%ProgramFiles%"
Run Code Online (Sandbox Code Playgroud)


小智 5

为了使用 PowerShell 详细说明Heyvoon 的回复(2015-06-08),这个简单的 PowerShell 脚本应该为您提供有关 %path% 的详细信息:

$env:Path -split ";" | % {"$(test-path $_); $_"}
Run Code Online (Sandbox Code Playgroud)

它正在生成这种您可以独立验证的输出:

True;C:\WINDOWS
True;C:\WINDOWS\system32
True;C:\WINDOWS\System32\Wbem
False;C:windows\System32\windowsPowerShell\v1.0\
False;C:\Program Files (x86)\Java\jre7\bin
Run Code Online (Sandbox Code Playgroud)

重新组装以更新路径:

$x = $null; foreach ($t in ($env:Path -split ";") ) {if (test-path $t) {$x += $t + ";"}}; $x
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

107547 次

最近记录:

6 年,3 月 前