如何在Windows批处理文件中测试路径是文件还是目录?

19

我在这里搜索了一下,发现有人使用了这个方法

set is_dir=0
for %%i in ("%~1") do if exist "%%~si"\nul set is_dir=1

但是没有起作用,当%1==c:\this is a file with spaces.csproj时,测试仍然成功,这意味着它仍将被视为文件夹!!!

有人知道答案,我猜这是一个非常普遍的问题,Windows存在已经很多年了,应该有一个非常简单的解决方案....


2
你的问题是关于 MS-DOS 还是 Windows 中的命令提示符?而且是哪个版本? - wimh
我的测试环境是Windows 7 x64和Windows XP,我没有在其他平台上尝试过。谢谢。 - aaron
7个回答

41

我知道在MS-DOS中,if exist path\nul用于测试文件夹是否存在。但我不确定在长文件名出现后是否还有效。

我知道在Windows批处理中,if exist "long path\nul"无法工作。直到今天,我才意识到只要路径以短的8.3形式呈现,if exist path\nul就可以在Vista及以上版本上工作。

原始代码似乎在Vista上可以正常工作。它看起来也应该在XP上工作,但我相信以下XP bug会导致问题:Batch parameter %~s1 gives incorrect 8.3 short name

原始代码不需要使用FOR循环,它可以简单地使用%~s1

这里是一个变体,可以完全将路径分类为INVALID(无效)、FILE(文件)或FOLDER(文件夹)。它适用于Vista,但由于%~s1的bug,不适用于XP。我不确定在MS-DOS上的表现如何。
2015年12月08日更新:在许多Windows情况下,此方法都失败了

@echo off
if not exist "%~1" ( set "type=INVALID" ) else if exist %~s1\nul ( set "type=FOLDER" ) else ( set "type=FILE" )
@echo "%~1" = %type%

我相信这种变体可以在几乎所有版本的 Microsoft 批处理中使用,包括 MS-DOS 和 XP。(显然,它不会在不支持 PUSHD 的早期 DOS 版本上运行)

@echo off
if exist "%~1" (2>nul pushd "%~1" && (popd&set "type=FOLDER") || set "type=FILE" ) else set "type=INVALID"
echo "%~1" = %type%

更新时间:2014年12月26日

我非常确信以下内容适用于从XP开始的所有Windows版本,但是我只在Win 7上进行过测试。
编辑于2015年12月08日:这可能会在网络驱动器上失败,因为文件夹测试可以错误地将文件报告为文件夹。

@echo off
if exist %1\ (
  echo %1 is a folder
) else if exist %1 (
  echo %1 is a file
) else (
  echo %1 does not exist
)

更新于2015-12-08

终于,这个测试可以在任何Windows版本上运行,包括使用网络驱动器和UNC路径。

for /f "tokens=1,2 delims=d" %%A in ("-%~a1") do if "%%B" neq "" (
  echo %1 is a folder
) else if "%%A" neq "-" (
  echo %1 is a file
) else (
  echo %1 does not exist
)

注意 - 此技术旨在用于没有通配符(单个特定文件或文件夹)的路径。 如果提供的路径包含一个或多个通配符,则它会为文件系统遇到的第一个文件或文件夹提供结果。 根据底层文件系统(FAT32、NTFS 等),相同的目录结构可能会给出不同的排序结果。


@dbenham:关于2014年的更新的简短说明,把%1\ %1部分放在引号中不是个好主意吗? - zb226
1
@zb226 - 不需要添加额外的引号。参数应该已经被引用了,否则您首先就无法收到它。请注意,在任何引号之外放置 \ 是无害的。"c:\path\""c:\path"\是等效的。如果您确实想显式添加自己的引号,则必须使用"%~1""%~1\" - dbenham
@dbenham:啊,现在我明白原理了——因为它是用户输入的即 %1…我现在正在处理另一种情况,即测试文件名在一个常规变量中,那么当然,引号很重要。无论如何,感谢您的澄清! - zb226
1
@zb226 - 抱歉,反斜杠格式问题搞乱了我的注释。我的意思是说,在引号内的路径可以在闭合引号后面加上反斜杠,它的作用与如果反斜杠在引号内完全相同。 - dbenham
最后一次更新的for循环在Win 10 18990上不起作用 :-( 为什么对于完全不同的用户,完全相同的批处理文件会产生完全不同的结果?多年来,我因为这种愚蠢而浪费了大量时间...而所有这些都是在试图通过自动化复杂任务来节省时间! - Kenny83
显示剩余8条评论

9
我刚刚尝试了这种方法。希望能有所帮助。
@ECHO OFF
SET CURR_DIR=%CD%
SET IS_DIR=0
CD %1% 
IF "%ERRORLEVEL%"=="0" SET IS_DIR=1
CD %CURR_DIR%
ECHO IS DIRECTORY %IS_DIR% 

输出:

D:\Work\Stand alone Java classes>test.bat D:\Work\Training
IS DIRECTORY 1

D:\Work\Stand alone Java classes>test.bat D:\Work\Training\SRT.txt
The directory name is invalid.
IS DIRECTORY 0

3
太好了,谢谢!我把它缩短了一点:PUSHD %1 2>NUL && SET IS_DIR=1 || SET IS_DIR=0 & POPD - aaron

2

“dir” 命令的 /ad 选项会列出文件夹,“/b” 选项则为裸格式。假设您已经检查了文件是否存在,请使用以下命令:

dir /ad /b ChangeThisToYourFilename 1> NUL 2> NUL
if %ERRORLEVEL% EQU 0 (
    echo is a folder
) else (
    echo is NOT a folder
)

2
如果将文本更改为“是文件夹”或“不是文件夹”,则可以正常工作。这是一种有趣的方法。虽然不直观,但它能够正常工作,因为文件夹中始终可用 . 和 .. 目录。而文件则没有。 - dbenham
1
更简单的语法-同样的解决方案:dir /ad /b "somePath" 1>nul 2>nul && (echo 是一个文件夹) || (echo 不是一个文件夹) - dbenham
如果文件夹包含许多子文件夹,执行此操作可能需要一些时间。 - aaron

1
对于一行代码:

dir /a:d /b C:\Windows 2>&1 | findstr /i /n /c:"File Not Found">nul && (@echo. Im a file) || (@echo. Im a folder)

例如,将 C:\Windows 更改为 C:\Windows\Notepad.exe

-抱歉 Arun,dbenham,我没有看到你们的!和你们一样。


这个可能存在本地化问题? - aaron
没有findstr,它甚至更短: dir /a:d /b C:\Windows 2>nul >nul && (@echo. 我是一个文件) || (@echo. 我是一个文件夹) - Radon8472

1

之前,我使用了 "\nul" 方法,但很长一段时间以来,我已经开始使用 "\*" 来测试现有的文件名是否是文件夹或文件。据我所知,它适用于所有 Windows 版本,从 Windows 95(也许更早的版本)到当前所有的 Windows 版本。

因此,与其他方法一样,首先检查文件是否存在。然后,要查看它是否是 "Folder",请使用以下命令进行测试:if exist "%fspec%\*":

if not exist "%fspec%"   goto :NotExistOrInvalid

rem "%fspec%" is "Valid" and is either a "Folder", or a "File".
if     exist "%fspec%\*" goto :IsValidAndIsAFolder

rem "%fspec%" is a "File" (a "Regular File" or a Shortcut/Link).
goto :IsValidAndIsAFile

例如:

set "fspec=XYZ:%perlpath%"
if not exist "%fspec%" echo "%fspec%": Invalid or not found && rem Invalid, goto :NotExistOrInvalid

set "fspec=%perlpath%"
if not exist "%fspec%" echo "%fspec%": Invalid or not found && rem goto :NotExistOrInvalid

rem "%fspec%" Is a "Valid" filespec and is either a "Folder", or a "File".
if exist "%fspec%\*" (echo "%fspec%" is a "Folder".) else echo "%fspec%" is a "File".

set "fspec=%perlpath%\perl.exe"
if not exist "%fspec%" echo "%fspec%": Invalid or not found && rem Invalid, goto :NotExistOrInvalid

rem "%fspec%" Is a "Valid" filespec and is either a "Folder", or a "File".
if exist "%fspec%\*" (echo "%fspec%" is a "Folder".) else echo "%fspec%" is a "File".

这段文字的翻译是:“输出结果如下:”。
"XYZ:F:\usr\perl\bin": Invalid or not found
"F:\usr\perl\bin" is a "Folder".
"F:\usr\perl\bin\perl.exe" is a "File".

1

这个解决方案结合了文件属性参数扩展 (%~a1) 和 变量子字符串提取 (%variable:~0,1%):

@ECHO OFF

CALL :is_directory C:\Windows
CALL :is_directory C:\MinGW\share\doc\mingw-get\README
CALL :is_directory C:\$Recycle.Bin
CALL :is_directory "C:\Documents and Settings"
CALL :is_directory "%LOGONSERVER%\C$\Users\All Users"

GOTO :EOF

:is_directory
    SETLOCAL
    SET file_attribute=%~a1
    IF "%file_attribute:~0,1%"=="d" (
        ECHO %file_attribute% %1 is a directory
    ) ELSE (
        ECHO %file_attribute% %1 is NOT a directory
    )
    ENDLOCAL
    GOTO :EOF

输出:

d-------- C:\Windows is a directory
--a------ C:\MinGW\share\doc\mingw-get\README is NOT a directory
d--hs---- C:\$Recycle.Bin is a directory
d--hs---l "C:\Documents and Settings" is a directory
d--hs---l "\\MYCOMPUTER\C$\Users\All Users" is a directory

0
我认为缺少的是官方文档中对该函数的定义。实际上,我期望cmd.exe的基本内置函数应该返回在给定情况下是否存在具有该名称的文件或文件夹,然而,文件与文件夹的区分是更高级函数的目的,例如dir...因此,这些要求可以通过其他方式满足,有很多...例如,我现在在2023年以\结尾的文件夹。
@echo off
IF EXIST %OneDriveCommercial%\desktop\ (
    copy "%appdata%\Microsoft\Windows\Start Menu\Programs\Work Resources (RADC)\*.lnk" "%OneDriveCommercial%\desktop\"
    echo Copied RDAC to %OneDriveCommercial%\desktop
) else (
    copy "%appdata%\Microsoft\Windows\Start Menu\Programs\Work Resources (RADC)\*.lnk" "%userprofile%\desktop"
    echo Copied RDAC to %userprofile%\desktop
)

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