如何检查 PATH 环境变量中是否已经存在目录?这是一个开始。不过,我对下面的代码所做的只是回显 %PATH% 中的第一个目录。由于这是一个 FOR 循环,您会认为它会枚举 %PATH% 中的所有目录,但它只获取第一个。
有没有更好的方法来做到这一点?像在 %PATH% 变量上运行的 find 或 findstr 之类的东西?我只想检查 %PATH% 中的目录列表中是否存在一个目录,以避免添加可能已经存在的内容。
FOR /F "delims=;" %%P IN ("%PATH%") DO (
@ECHO %%~P
)
最佳答案
首先我将指出一些使这个问题难以完美解决的问题。然后我将介绍我能想出的最防弹的解决方案。
在本次讨论中,我将使用小写路径表示文件系统中的单个文件夹路径,并使用大写 PATH 表示 PATH 环境变量。
从实际的角度来看,大多数人想知道 PATH 是否包含给定路径的逻辑等效项,而不是 PATH 是否包含给定路径的精确字符串匹配。这可能有问题,因为:
\
在路径中是可选的 大多数路径在使用和不使用尾随
\
时同样有效.无论哪种方式,路径在逻辑上都指向相同的位置。 PATH 经常包含带有和不带有尾随 \
的路径的混合。 .这可能是在 PATH 中搜索匹配项时最常见的实际问题。C:
(意思是 C 盘的当前工作目录)与 C:\
非常不同(即C盘根目录)任何不符合旧 8.3 标准的路径都有一个符合标准的替代短格式。这是我经常看到的另一个 PATH 问题,尤其是在商业环境中。
/
和 \
作为路径中的文件夹分隔符。 这并不常见,但可以使用
/
指定路径。而不是 \
并且它会在 PATH 中正常运行(以及在许多其他 Windows 上下文中)C:\FOLDER\\和 C:\FOLDER\是等价的。这实际上在处理路径时在许多情况下都有帮助,因为开发人员通常可以附加
\
到路径而无需检查尾随 \
已经存在。但是,如果尝试执行精确的字符串匹配,这显然会导致问题。C:
,不同于 C:\
,但是 C:\
(有效路径),不同于 C:\\
(无效路径)。"C:\test. "
相当于 "C:\test"
. .\
和 parent ..\
文件夹说明符可能出现在路径 中在现实生活中不太可能看到,但类似于
C:\.\parent\child\..\.\child\
相当于 C:\parent\child
路径通常用引号括起来以防止特殊字符,如
<space>
,
;
^
&
=
.实际上,任何数量的引号都可以出现在路径之前、之内和/或之后。 Windows 会忽略它们,除非是为了防止特殊字符。除非路径包含 ;
,否则 PATH 中永远不需要引号,但引号可能永远存在。 完全限定的路径正好指向文件系统中的一个特定位置。相对路径位置根据当前工作卷和目录的值而变化。相对路径有三种主要风格:
D:
相对于卷 D 的当前工作目录:\myPath
是相对于当前工作量(可以是 C:、D: 等)myPath
相对于当前工作卷和目录 在 PATH 中包含相对路径是完全合法的。这在 Unix 世界中很常见,因为 Unix 默认不搜索当前目录,所以 Unix PATH 通常会包含
.\
.但是 Windows 默认会搜索当前目录,因此 Windows PATH 中很少有相对路径。 因此,为了可靠地检查 PATH 是否已经包含路径,我们需要一种将任何给定路径转换为规范(标准)形式的方法。
~s
FOR 变量和参数扩展使用的修饰符是解决问题 1 - 6 和部分解决问题 7 的简单方法。 ~s
修饰符删除封闭引号,但保留内部引号。通过在比较之前从所有路径中明确删除引号,可以完全解决问题 7。请注意,如果路径实际上不存在,则 ~s
修饰符不会附加 \
到路径,也不会将路径转换为有效的 8.3 格式。~s
的问题是否将相对路径转换为完全限定的路径。这对于问题 8 来说是有问题的,因为相对路径不应该与完全限定的路径匹配。我们可以使用 FINDSTR 正则表达式将路径分类为完全限定的或相对的。正常的完全限定路径必须以 <letter>:<separator>
开头但不是 <letter>:<separator><separator>
,其中 \
或 /
. UNC 路径始终是完全限定的,并且必须以 \\
开头.在比较完全限定的路径时,我们使用 ~s
修饰符。在比较相对路径时,我们使用原始字符串。最后,我们从不将完全限定的路径与相对路径进行比较。此策略为问题 8 提供了一个很好的实用解决方案。唯一的限制是两个逻辑上等效的相对路径可能被视为不匹配,但这是一个次要问题,因为相对路径在 Windows PATH 中很少见。还有一些其他问题使这个问题复杂化:
9) 处理包含特殊字符的 PATH 时,正常扩展是不可靠的。
特殊字符不需要在 PATH 中引用,但可以引用。所以一个 PATH 像
C:\THIS & THAT;"C:\& THE OTHER THING"
是完全有效的,但不能使用简单的扩展安全地扩展它,因为 "%PATH%"
和 %PATH%
将失败。10) 路径分隔符在路径名 中也有效
一个
;
用于在 PATH 中分隔路径,但 ;
也可以是路径中的有效字符,在这种情况下必须引用路径。这会导致解析问题。jeb 在 'Pretty print' windows %PATH% variable - how to split on ';' in CMD shell 解决了问题 9 和 10
所以我们可以结合
~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
该函数可以像这样使用(假设批处理文件名为 inPath.bat):
set test=c:\mypath
call inPath test && (echo found) || (echo not found)
通常,检查 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
关于windows - 如何检查 %PATH% 中是否存在目录?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/141344/