如何检查%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匹配。 这可能是有问题的,因为:
-
在path中,结尾
\
是可选的
大多数path在有和没有尾随的情况下都工作得很好。 path从逻辑上指向相同的位置。 PATH通常具有和不具有拖尾\
的path的混合。 searchPATH进行匹配时,这可能是最常见的实际问题。- 有一个例外:相对path
C:
意思是驱动器C的当前工作目录)与C:\
(意思是驱动器C的根目录)
- 有一个例外:相对path
-
一些path具有替代的短名称
任何不符合旧8.3标准的path都有一个符合标准的替代简称。 这是另一个PATH问题,我已经看到了一些频率,特别是在商业环境。 -
Windows在path中同时接受
/
和\
作为文件夹分隔符。
这是不常见的,但是一个path可以使用/
而不是\
来指定,并且在PATH中(以及在许多其他的Windows环境中) -
Windows将连续的文件夹分隔符视为一个逻辑分隔符。
C:\ FOLDER \\和C:\ FOLDER \是等效的。 这在处理path时在很多情况下都有帮助,因为开发人员通常可以将path添加到path中,而不必检查尾部是否已经存在。 但是,如果试图执行精确的string匹配,这显然会造成问题。- 例外情况:不仅
C:
与C:\
不同,而且C:\
(有效path)不同于C:\\
(无效path)。
- 例外情况:不仅
-
Windows从文件和目录名称中修剪尾随点和空格。
"C:\test. "
相当于"C:\test"
。 -
当前
.\
和父..\
文件夹说明符可能会出现在path中
不太可能在现实生活中看到,但是像C:\.\parent\child\..\.\child\
等于C:\parent\child
-
path可以可选地包含在双引号内。
path通常用引号括起来以防止像<space>
这样的特殊字符;
^
&
=
。 实际上,任何数量的引号可以出现在path之前,之内和/或之后。 除了用于防止特殊字符之外,它们被Windows忽略。 除非path包含a;
否则引号在PATH中是不需要的;
,但报价可能是永远不会有的。 -
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)。
使用for
和delims
,你不能捕获任意数量的字段(就像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
这返回TRUE
或FALSE
简单,简单,容易!
作为替代scheme:
-
在要search
PATH
variables的文件夹中,使用这样一个不寻常的名称创build临时文件,以至于您永远不会期望计算机上的任何其他文件具有该文件。 -
使用标准批处理脚本构造,可以通过查找由某个环境variables(通常为
PATH
)定义的目录列表来执行文件search。 -
检查search结果是否与所讨论的path匹配,并显示结果。
-
删除临时文件。
这可能看起来像这样:
@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 )
已知的问题:
-
该方法只能在目录存在的情况下工作(情况并非总是如此)。
-
创build/删除目录中的文件会影响其“修改的date/时间”属性(有时这可能是一个不良影响)。
-
在脑海中创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