使用DOS批处理脚本获取特定文件的父目录名称

23
我需要在DOS中找到文件的父目录名称。
例如,假设这是目录。
C:\test\pack\a.txt

我有一个脚本,它会要求我输入文件名。

C:\\>getname.bat     
enter file name: c:\test\pack\a.txt   

现在脚本应该只返回文件的父级名称。

pack           
并且不是整个父级路径到文件。
c:\test\pack   
10个回答

25

获取批处理文件的父文件夹可能非常简单:

@echo off
for %%a in ("%~dp0\.") do set "parent=%%~nxa"
echo %parent%

对于问题中给出的文件路径的父级路径:

@echo off
for %%a in ("c:\test\pack\a.txt") do for %%b in ("%%~dpa\.") do set "parent=%%~nxb"
echo %parent%

3
在查找批处理文件几个目录级别之上的父文件夹时,我发现这个方法很有用。只需在 %~dp0\. 后面添加 \.. 即可返回一个更高层级的文件夹。例如:for %%a in ("%~dp0\.\..\..\..") do set "parent=%%~nxa" - Matt
提示其他人:您可能需要使用("%~p1\.")而不是("%~dp0\.") - Venryx
2
虽然在学术上很有趣,但这并没有回答OP的问题。我处于同样的情况,正在从C:\A\B\C.Bat运行批处理文件,该文件正在处理文件C:\D\E\Foo.txt - 我不关心批处理文件本身的路径,我关心从文本文件的路径中获取E - dgnuff

13

如果父目录名称包含空格,则上面的第一个答案无效。以下内容有效:

@echo off
setlocal

set ParentDir=%~p1
set ParentDir=%ParentDir: =:%
set ParentDir=%ParentDir:\= %
call :getparentdir %ParentDir%
set ParentDir=%ParentDir::= %

echo ParentDir is %ParentDir%
goto :EOF

:getparentdir
if "%~1" EQU "" goto :EOF
Set ParentDir=%~1
shift
goto :getparentdir

使用参数"C:\Temp\Parent Dir With Space\myfile.txt"调用上述函数会得到以下结果:

>GetParentDir "C:\Temp\Parent Dir With Space\myfile.txt"
ParentDir is Parent Dir With Space

以上代码通过将空格替换为冒号(在Windows路径中不应存在),然后将目录分隔符替换为空格,以便将单独的目录作为单独的参数传递给getparentdir函数。getparentdir函数会循环直到找到最后一个参数。最后,结果中的任何冒号都被替换为空格。


这个程序能否处理包含逗号的文件名呢?我知道这是一个特殊情况,只是想知道这是否可以简单地添加或需要大量额外的代码。谢谢。 - GeorgeCostanza

12

请参考这个问题。

@echo OFF
set mydir="%~p1"
SET mydir=%mydir:\=;%
for /F "tokens=* delims=;" %%i IN (%mydir%) DO call :LAST_FOLDER %%i goto :EOF
:LAST_FOLDER if "%1"=="" ( @echo 最后一个文件夹名:%LAST% goto :EOF )
set LAST=%1 SHIFT
goto :LAST_FOLDER

我不知道那能怎么行得通。 %p1 对我无效。它只能与 %dp0 或 %~p0 一起使用。 - djangofan
@djangofan 这个脚本以路径作为参数,并返回该路径的目录名。 - asdfg
好的,我在下面添加了一个更简单地解决问题的答案。 - djangofan

1

你可以使用VBScript,例如将以下内容保存为getpath.vbs

Set objFS = CreateObject("Scripting.FileSystemObject")
Set objArgs = WScript.Arguments
strFile = objArgs(0)
WScript.Echo objFS.GetParentFolderName(strFile)

然后在命令行或批处理中执行以下操作

C:\test>cscript //nologo getpath.vbs c:\test\pack\a.txt
c:\test\pack

如果你想要批处理方法,可以查看 for /?
  %~fI        - expands %I to a fully qualified path name
  %~dI        - expands %I to a drive letter only
  %~pI        - expands %I to a path only

1
我发现djangofan和paranoid的回答结合起来,既简单又足够完美,当我查找我的脚本的父目录时使用了这种方法:
set FULL_PATH=%~dp0
set FULL_PATH=%FULL_PATH:~1,-1%
for %%i in ("%FULL_PATH%") do set "PARENT_FOLDER=%%~ni"
echo %PARENT_FOLDER%

由于您想要处理用户输入,您需要进行一些最小的额外工作,以处理类似 C:\foo\bar\a.txt 和 C:\foo\bar\a.txt 或 c:/foo/bar/a.txt 的合法变化。下面的代码可能对您有帮助:

@setlocal
@echo off

call:GET_PARENT_FOLDER C:\foo\bar\a.txt
echo %PARENT_FOLDER%
call:GET_PARENT_FOLDER C:\foo\bar\\a.txt
echo %PARENT_FOLDER%
call:GET_PARENT_FOLDER c:/foo/bar/a.txt
echo %PARENT_FOLDER%

pause
goto:EOF

:GET_PARENT_FOLDER
:: Strip the filename, so we get something like this: 'C:\foor\bar\'
set "_FULL_PATH=%~dp1"

:: Strips all dangling '\' and '/' in a loop, so the last folder name becomes accessible
:_STRIP
if not "%_FULL_PATH:~-1%"=="\" if not "%_FULL_PATH:~-1%"=="/" goto:_STRIP_END
set "_FULL_PATH=%_FULL_PATH:~1,-1%"
goto:_STRIP
:_STRIP_END

:: We need the context of a for-loop for the special path operators to be available
for %%i in ("%_FULL_PATH%") do set "PARENT_FOLDER=%%~ni"

goto:EOF

1
这是一种不使用CALL的方式,我认为它更快。基于jeb的分割函数(如果目录名包含!可能会失败):
@echo off

set "mydir=%~p1"
SET mydir=%mydir:~0,-1%

setlocal EnableDelayedExpansion
set LF=^


rem ** Two empty lines are required

for %%L in ("!LF!") DO (
    set "dir_name=!mydir:\=%%L!"
)
for /f "delims=" %%P in (""!dir_name!"") do set "dn=%%~P"
echo %dn%

exit /b 0 

1
DaDummy的想法在更实用的可重复使用功能中得到了实现。现在,_full_path的第一个字符也已包含在内。
set map=D:\test1\test2\test3\test4.txt
call:get_parent_path "%map%"
echo full_path is %_full_path%
call:get_parent_path %_full_path%
echo full_path is %_full_path%
call:get_last_path %_full_path%
echo last_path is %_last_path%
goto :eof

:get_parent_path
set "_full_path=%~dp1"
:_strip
if not "%_full_path:~-1%"=="\" if not "%_full_path:~-1%"=="/" goto:_strip_end
set "_full_path=%_full_path:~0,-1%"
goto:_strip
:_strip_end
exit /b

:get_last_path
set "_last_path=%~nx1"
exit /b

::result:
::full_path is D:\test1\test2\test3
::full_path is D:\test1\test2
::last_path is test2

0

这里有另一种解决方案:

SET LAST=%CD%
SET PARENTNAME=NONE
cd /D C:\test\pack
FOR %%I in (%CD%) do SET PARENTNAME=%%~nI
cd /D %LAST%
ECHO %PARENTNAME%

%%~nI:'~n' 从存储在 %%I 变量中的路径中提取名称
cd:'/D' 参数也可用于在磁盘之间切换


0
call :set_dirname "%cd%"
echo %DIRNAME%


:set_dirname
set DIRNAME=%~n1
goto :eof

2
尽管这段代码可能解决了问题,但加上解释说明它是如何解决问题的将有助于提高您的帖子质量,并可能产生更多的赞同票。请记住,您回答问题是为了未来的读者,而不仅仅是现在提问的人。请编辑您的答案以添加解释,并指出什么样的限制和假设适用。来自审核 - double-beep

0

以下是一种检索通过CMD命令访问的文件所在文件夹名称的方法。使用OP的示例,文件路径为C:\test\pack\a.txt,您希望您的命令提取出最后一个文件夹名称"pack"。(正如dgnuff在评论中指出的那样,目前得分最高的答案是针对不同问题的答案,如果您处于OP的情况下,它将无法帮助您。)

我使用嵌套的FOR循环来实现它。以下是一个示例,命令将出现在批处理文件中,而不是直接输入到命令行中:

FOR %%Z in ("*.txt") do (FOR %%I in ("%%~dpZ.") do (copy "%%~Z" "%%~dpZ%%~nxI.archive\%%~nxZ" ))

这个示例批处理命令将文本文件复制到以文件夹名称命名的子文件夹中:

C:\test\pack\a.txt -> C:\test\pack\pack.archive\a.txt
C:\test\pick\b.txt -> C:\test\pick\pick.archive\b.txt
E:\A\B\C\D\foo.txt -> E:\A\B\C\D\D.archive\foo.txt

外部 FOR 循环用于复制文件。但是你需要内部 FOR 循环来建立目标路径,因为目标子文件夹的名称基于源文件夹的名称。序列 "%%~dpZ." — Z 后面跟着那个点 — 检索包含当前文件的文件夹的名称,然后可以通过参数 I 引用它(而 Z 是对正在被复制的文件的引用)。参见 this page 中的“参数扩展”部分以查看用于构建路径的内置变量的详细信息,例如 "%%~dpZ%%~nxI.archive\%%~nxZ"


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