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

如何检查一个目录是否已经存在于PATH环境variables中? 这是一个开始。 但是,我已经设法使用下面的代码来回显%PATH%中的第一个目录。 因为这是一个FOR循环,所以你会认为它会枚举%PATH%中的所有目录,但是它只能得到第一个。

有没有更好的方法来做到这一点? 像find或findstr在%PATH%variables上运行? 我只想检查目录是否存在于%PATH%目录中,以避免添加可能已经存在的内容。

FOR /F "delims=;" %%P IN ("%PATH%") DO ( @ECHO %%~P ) 

首先我会指出一些问题,使这个问题难以完全解决。 然后,我将介绍我已经能够提出的最防弹解决scheme。

对于本次讨论,我将使用小写path来表示文件系统中的单个文件夹path,并使用大写PATH来表示PATH环境variables。

从实际的angular度来看,大多数人想知道PATH是否包含给定path的逻辑等价物,而不是PATH是否包含给定path的精确string匹配。 这可能是有问题的,因为:

  1. 在path中,结尾\是可选的
    大多数path在有和没有尾随的情况下都工作得很好。 path从逻辑上指向相同的位置。 PATH通常具有和不具有拖尾\的path的混合。 searchPATH进行匹配时,这可能是最常见的实际问题。

    • 有一个例外:相对pathC:意思是驱动器C的当前工作目录)与C:\ (意思是驱动器C的根目录)
  2. 一些path具有替代的短名称
    任何不符合旧8.3标准的path都有一个符合标准的替代简称。 这是另一个PATH问题,我已经看到了一些频率,特别是在商业环境。

  3. Windows在path中同时接受/\作为文件夹分隔符。
    这是不常见的,但是一个path可以使用/而不是\来指定,并且在PATH中(以及在许多其他的Windows环境中)

  4. Windows将连续的文件夹分隔符视为一个逻辑分隔符。
    C:\ FOLDER \\和C:\ FOLDER \是等效的。 这在处理path时在很多情况下都有帮助,因为开发人员通常可以将path添加到path中,而不必检查尾部是否已经存在。 但是,如果试图执行精确的string匹配,这显然会造成问题。

    • 例外情况:不仅C:C:\不同,而且C:\ (有效path)不同于C:\\ (无效path)。
  5. Windows从文件和目录名称中修剪尾随点和空格。
    "C:\test. "相当于"C:\test"

  6. 当前.\和父..\文件夹说明符可能会出现在path中
    不太可能在现实生活中看到,但是像C:\.\parent\child\..\.\child\等于C:\parent\child

  7. path可以可选地包含在双引号内。
    path通常用引号括起来以防止像<space>这样的特殊字符; ^ & = 。 实际上,任何数量的引号可以出现在path之前,之内和/或之后。 除了用于防止特殊字符之外,它们被Windows忽略。 除非path包含a ;否则引号在PATH中是不需要的; ,但报价可能是永远不会有的。

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

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

    在PATH中包含相对path是完全合法的。 这在Unix世界非常普遍,因为Unix默认不search当前目录,因此Unix PATH通常会包含.\ 。 但Windows默认search当前目录,所以相对path在Windows PATH中很less见。

因此,为了可靠地检查PATH是否已经包含path,我们需要一种将任何给定path转换为规范(标准)forms的方法。 FORvariables和参数扩展使用的〜s修饰符是解决问题1-6的简单方法,部分解决了问题7.〜s修饰符删除封闭引号,但保留内部引号。 问题7可以通过在比较之前明确删除所有path中的引号来完全解决。 请注意,如果path没有物理存在,那么〜s修饰符不会将\追加到path中,也不会将path转换为有效的8.3格式。

〜s的问题是它将相对path转换为完全合格的path。 这对问题8是有问题的,因为相对path不应该匹配完全限定的path。 我们可以使用FINDSTR正则expression式将path分类为完全限定或相对限制。 正常的完全限定path必须以<letter>:<separator> <letter>:<separator><separator>但不能以<letter>:<separator><separator> ,其中<separator>是\/ 。 UNCpath始终完全合格,必须以\\开头。 比较完全合格的path时,我们使用〜s修饰符。 比较相对path时,我们使用原始string。 最后,我们从不比较完全合格的path到相对path。 此策略为问题8提供了一个很好的实际解决scheme。唯一的限制是两个逻辑上等价的相对path可以被视为不匹配,但这是一个小问题,因为在Windows PATH中相对path很less。

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

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

10) path分隔符在path名中也是有效的
A ; 用于在PATH中分隔path,但是; 也可以是path中的有效字符,在这种情况下path必须被引用。 这导致parsing问题。

jeb在'Pretty print'窗口中解决了问题9和10 %PATH%variables – 如何分割';' 在CMD shell中

所以我们可以将〜s修饰符和path分类技术以及jeb的PATHparsing器的变体结合起来,以得到这个近乎满贯的解决scheme来检查PATH中是否存在给定的path。 该function可以在batch file中包含和调用,也可以独立使用,并作为自己的inPath.batbatch file调用。 它看起来像很多代码,但超过一半是评论。

 @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 

该函数可以像这样使用(假设batch file名为inPath.bat):

 set test=c:\mypath call inPath test && (echo found) || (echo not found) 


通常,检查PATH中是否存在path的原因是因为如果path不存在,则需要追加path。 这通常通过使用诸如path %path%;%newPath% 。 但问题9展示了这是不是可靠的。

另一个问题是如何在函数结束时返回ENDLOCAL屏障上的最终PATH值,特别是如果可以使用延迟扩展启用或禁用该function。 任何非转义的! 如果启用延迟扩展将会损坏该值。

这些问题可以通过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 

我有一段时间没有做任何batch file编程,但是:

 echo ;%PATH%; | find /C /I ";<string>;" 

应该给你0,如果没有findstring,如果它是1或更多。

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

另一种检查path中是否有东西的方法是执行一些不会失败的无害可执行文件,并检查结果。 作为一个例子,下面的代码片段检查maven是否在path中:

 mvn --help > NUL 2> NUL if errorlevel 1 goto mvnNotInPath 

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

使用fordelims ,你不能捕获任意数量的字段(就像Adam指出的那样),所以你必须使用循环技术。 以下命令脚本将在单独的行中列出PATH环境variables中的每个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 

它模拟了一个古典的… wend构造在许多编程语言中find。 有了这个,你可以使用类似findstr东西来随后过滤和寻找一个特定的path。 例如,如果将上面的脚本保存在一个名为tidypath.cmd的文件中,那么以下是如何pipe理findstr ,在标准程序目录下查找path(使用不区分大小写的匹配):

 > tidypath | findstr /i "%ProgramFiles%" 

在阅读这里的答案之后,我想我可以提供一个新的观点:如果这个问题的目的是要知道某个可执行文件的path是否存在于%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" ) 

如果您不知道可执行文件的扩展名,只需查看它们全部:

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

这将寻找一个确切的,但不区分大小写的匹配,所以请注意任何尾随反斜杠等:

 for %P in ("%path:;=";"%") do @if /i %P=="PATH_TO_CHECK" echo %P exists in PATH 

或者在一个batch file(例如checkpath.bat)中使用一个参数:

 @for %%P in ("%path:;=";"%") do @if /i %%P=="%~1" echo %%P exists in PATH 

在后一种forms中,可以调用例如checkpath "%ProgramFiles%"来查看指定的path是否已经存在于PATH中。

请注意,这个实现假设没有分号或单引号内存在一个path项目。

我使用for循环来实现你的实现,并将其扩展成遍历path的所有元素的东西。 for循环的每次迭代都从整个path(保存在%q和%r中)中删除path的第一个元素(%p)。

 @echo off SET MYPATHCOPY=%PATH% :search for /f "delims=; tokens=1,2*" %%p in ("%MYPATHCOPY%") do ( @echo %%~p SET MYPATHCOPY=%%~q;%%~r ) if "%MYPATHCOPY%"==";" goto done; goto search; :done 

示例输出:

 Z:\>path.bat C:\Program Files\Microsoft DirectX SDK (November 2007)\Utilities\Bin\x86 c:\program files\imagemagick-6.3.4-q16 C:\WINDOWS\system32 C:\WINDOWS C:\SFU\common\ c:\Program Files\Debugging Tools for Windows C:\Program Files\Nmap 

将目录添加到PATH(如果尚不存在):

 set myPath=c:\mypath For /F "Delims=" %%I In ('echo %PATH% ^| find /C /I "%myPath%"') Do set pathExists=%%I 2>Nul If %pathExists%==0 (set PATH=%myPath%;%PATH%) 

您也可以使用子stringreplace来testing是否存在子string。 在这里,我删除引号来创buildPATH_NQ,然后从PATH_NQ中删除“c:\ mydir”并将其与原始文件进行比较,以查看是否有任何更改:

 set PATH_NQ=%PATH:"=% if not "%PATH_NQ%"=="%PATH_NQ:c:\mydir=%" goto already_in_path set PATH=%PATH%;c:\mydir :already_in_path 

我已经结合了上面的一些答案来提出这个问题,以确保给定的path条目尽可能简洁,命令行上没有垃圾回声。

 set myPath=<pathToEnsure | %1> echo ;%PATH%; | find /C /I ";%myPath%;" >nul if %ERRORLEVEL% NEQ 0 set PATH=%PATH%;%myPath% 

如果你的问题是“为什么不这个cmd脚本片段工作? 那么答案是for /f迭代行。 分隔delims将行分割为字段,但是您只捕获%%P的第一个字段。 用for /f循环无法捕获任意数量的字段。

build立在rcar的答案上,你必须确保没有find目标的子串。

 if a%X%==a%PATH% echo %X% is in PATH echo %PATH% | find /c /i ";%X%" if errorlevel 1 echo %X% is in PATH echo %PATH% | find /c /i "%X%;" if errorlevel 1 echo %X% is in PATH 

你提到你想避免添加目录到searchpath,如果它已经存在那里。 您打算将目录永久存储到path中,还是暂时存放batch file?

如果您希望将目录永久添加(或删除)到PATH,请查看Windows Resource Kit Tools中用于pipe理任务的Path Manager(pathman.exe)实用程序, http://support.microsoft.com/kb/927229 。 有了这个,你可以添加或删除系统和用户path的组件,它将处理exception,如重复的条目。

如果您只需要临时修改batch file的path,那么我只需在path前添加额外的path,由于path中的重复条目,可能会导致轻微的性能下降。

这个版本工作得很好。 它只是检查vim71是否在path中,如果不是,则将其前置。

 @echo off echo %PATH% | find /c /i "vim71" > nul if not errorlevel 1 goto jump PATH = C:\Program Files\Vim\vim71\;%PATH% :jump 

这个演示是为了说明错误级别的逻辑:

 @echo on echo %PATH% | find /c /i "Windows" if "%errorlevel%"=="0" echo Found Windows echo %PATH% | find /c /i "Nonesuch" if "%errorlevel%"=="0" echo Found Nonesuch 

在vim71代码中,逻辑是相反的,因为errorlevel 1相当于errorlevel> = 1。因此errorlevel 0总是计算true,所以使用“ not errorlevel 1 ”。

如果您使用setlocal和endlocal来定位您的环境设置,则可能不需要检查后记

 @echo off setlocal PATH = C:\Program Files\Vim\vim71\;%PATH% rem your code here endlocal 

在endlocal之后,你回到原来的path。

对“addPath”脚本的评论; 当提供一个空间的path,它抛出。

例如:调用addPath“c:\ Program Files(x86)\ Microsoft SDKs \ Windows \ v7.0A \ Bin”

产生:“文件”不被识别为内部或外部命令,可操作程序或batch file。

一般来说这是把一个EXE / DLL的path。 只要这个文件不会出现在其他地方:

 @echo off where /q <put filename here> if %errorlevel% == 1 ( setx PATH "%PATH%;<additional path stuff>" ) else ( echo "already set path" ) 

你可以使用PoweShell来完成这个工作。

 Test-Path $ENV:SystemRoot\YourDirectory Test-Path C:\Windows\YourDirectory 

这返回TRUEFALSE

简单,简单,容易!

作为替代scheme:

  1. 在要searchPATHvariables的文件夹中,使用这样一个不寻常的名称创build临时文件,以至于您永远不会期望计算机上的任何其他文件具有该文件。

  2. 使用标准批处理脚本构造,可以通过查找由某个环境variables(通常为PATH )定义的目录列表来执行文件search。

  3. 检查search结果是否与所讨论的path匹配,并显示结果。

  4. 删除临时文件。

这可能看起来像这样:

 @ECHO OFF SET "mypath=D:\the\searched-for\path" SET unusualname=nowthisissupposedtobesomeveryunusualfilename ECHO.>"%mypath%\%unusualname%" FOR %%f IN (%unusualname%) DO SET "foundpath=%%~dp$PATH:f" ERASE "%mypath%\%unusualname%" IF "%mypath%" == "%foundpath%" ( ECHO The dir exists in PATH ) ELSE ( ECHO The dir DOES NOT exist in PATH ) 

已知的问题:

  1. 该方法只能在目录存在的情况下工作(情况并非总是如此)。

  2. 创build/删除目录中的文件会影响其“修改的date/时间”属性(有时这可能是一个不良影响)。

  3. 在脑海中创build一个全球唯一的文件名不能被认为是非常可靠的。 生成这样一个名字本身并不是一件小事。

这个例程将searchpathvariables中的path\或file.ext,如果find,则返回0。 path\或文件可能包含空格如果引用。 如果一个variables作为最后一个parameter passing,它将被设置为d:\path\file

 @echo off&goto :PathCheck :PathCheck.CMD echo.PathCheck.CMD: Checks for existence of a path or file in %%PATH%% variable echo.Usage: PathCheck.CMD [Checkpath] or [Checkfile] [PathVar] echo.Checkpath must have a trailing \ but checkfile must not echo.If Checkpath contains spaces use quotes ie. "C:\Check path\" echo.Checkfile must not include a path, just the filename.ext echo.If Checkfile contains spaces use quotes ie. "Check File.ext" echo.Returns 0 if found, 1 if not or -1 if checkpath does not exist at all echo.If PathVar is not in command line it will be echoed with surrounding quotes echo.If PathVar is passed it will be set to d:\path\checkfile with no trailing \ echo.Then %%PathVar%% will be set to the fully qualified path to Checkfile echo.Note: %%PathVar%% variable set will not be surrounded with quotes echo.To view the path listing line by line use: PathCheck.CMD /L exit/b 1 :PathCheck if "%~1"=="" goto :PathCheck.CMD setlocal EnableDelayedExpansion set "PathVar=%~2" set "pth=" set "pcheck=%~1" if "%pcheck:~-1%" equ "\" ( if not exist %pcheck% endlocal&exit/b -1 set/a pth=1 ) for %%G in ("%path:;=" "%") do ( set "Pathfd=%%~G\" set "Pathfd=!Pathfd:\\=\!" if /i "%pcheck%" equ "/L" echo.!Pathfd! if defined pth ( if /i "%pcheck%" equ "!Pathfd!" endlocal&exit/b 0 ) else ( if exist "!Pathfd!%pcheck%" goto :CheckfileFound ) ) endlocal&exit/b 1 :CheckfileFound endlocal&( if not "%PathVar%"=="" ( call set "%~2=%Pathfd%%pcheck%" ) else (echo."%Pathfd%%pcheck%") exit/b 0 ) 

这是凯文·爱德华兹使用stringreplace的答案的变体。 基本模式是:

 IF "%PATH:new_path=%" == "%PATH%" PATH=%PATH%;new_path 

例如:

 IF "%PATH:C:\Scripts=%" == "%PATH%" PATH=%PATH%;C:\Scripts 

简而言之,我们在试图从PATH环境variables中删除/replacenew_path的情况下进行条件testing。 如果new_path不存在,则条件成功, new_path将首次附加到PATH 。 如果new_path已经存在,则条件失败,我们不会new_path添加new_path

为了详细说明使用Powershell的Heyvoon(2015.06.08)响应,这个简单的Powershell脚本应该给你详细的%path%

 $env:Path -split ";" | % {"$(test-path $_);$_"} 

产生这种你可以独立validation的输出

 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 

重新组装以便更新path:

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