从相对路径和/或文件名解析绝对路径


155

Windows批处理脚本中是否可以从包含文件名和/或相对路径的值返回绝对路径?

鉴于:

"..\"
"..\somefile.txt"

我需要相对于批处理文件的绝对路径。

例:

  • “ somefile.txt”位于“ C:\ Foo \”中
  • “ test.bat”位于“ C:\ Foo \ Bar”中。
  • 用户在“ C:\ Foo”中打开命令窗口并调用 Bar\test.bat ..\somefile.txt
  • 在批处理文件中,“ C:\ Foo \ somefile.txt”将来自 %1

1
相对路径并非故事的终点。还请考虑NTFS符号链接:最有可能的是,您还需要一个类似的符号realpath实现可靠的路径归一化。
ulidtko 2014年

可能您根本不需要确切的路径!您可以只添加基本路径:SET FilePath=%CD%\%1这样就可以像C:\Foo\Bar\..\..\some\other\dir\file.txt。程序似乎理解这种复杂的路径。
Fr0sT 2015年

这些答案中的许多都为复杂或仅仅是越野车而疯狂-但这实际上是一件很容易做的批量工作,请在下面查看我的答案
BrainSlugs83

Answers:


160

在批处理文件中,如在标准C程序中一样,参数0包含当前执行脚本的路径。您可以%~dp0用来仅获取第0个参数的路径部分(这是当前脚本)-该路径始终是完全限定的路径。

您还可以使用来获取第一个参数的完全限定路径%~f1,但这会根据当前的工作目录提供一个路径,这显然不是您想要的。

就个人而言,我经常%~dp0%~1在批处理文件中使用惯用语,该惯用语解释相对于正在执行的批处理路径的第一个参数。但是它确实有一个缺点:如果第一个参数完全合格,它就会失败。

如果需要同时支持相对路径绝对路径,则可以使用FrédéricMénez的解决方案:临时更改当前工作目录。

这是一个示例,将演示每种技术:

@echo off
echo %%~dp0 is "%~dp0"
echo %%0 is "%0"
echo %%~dpnx0 is "%~dpnx0"
echo %%~f1 is "%~f1"
echo %%~dp0%%~1 is "%~dp0%~1"

rem Temporarily change the current working directory, to retrieve a full path 
rem   to the first parameter
pushd .
cd %~dp0
echo batch-relative %%~f1 is "%~f1"
popd

如果将其另存为c:\ temp \ example.bat并从c:\ Users \ Public运行

c:\ Users \ Public> \ temp \ example.bat .. \ windows

...您将观察到以下输出:

%~dp0 is "C:\temp\"
%0 is "\temp\example.bat"
%~dpnx0 is "C:\temp\example.bat"
%~f1 is "C:\Users\windows"
%~dp0%~1 is "C:\temp\..\windows"
batch-relative %~f1 is "C:\Windows"

可以在以下位置找到批处理参数上允许使用的修饰符集的文档:https : //docs.microsoft.com/zh-cn/windows-server/administration/windows-commands/call


4
您可以处理%0%1同样:%~dpnx0为批处理文件本身的完全合格的路径+名称,%~dpnx1完全合格的路径+名称的第一个参数的[如果这是在所有的文件名。(但是,如果您始终不在命令行上提供完整的路径信息,那么您将如何命名其他驱动器上的文件?)
Kurt Pfeifle 2010年

这个例子比@frédéric-ménez答案要复杂得多
srossross 2014年

3
这在NTFS符号链接上失败。
ulidtko 2014年

@KurtPfeifle d:\foo\..\bar\xyz.txt仍然可以标准化。我建议将这种方法与下面的@ axel-heider答案结合使用(使用批处理子例程-然后您可以对任何变量(而不仅仅是数字变量)执行此操作)。
BrainSlugs83

@ulidtko您能详细说明吗?什么失败了?-您期望发生什么?-您是否希望解析到原始文件夹?(因为如果它确实,我会打电话的是一个失败-这是摆在首位有符号连接点的那种...)
BrainSlugs83

151

今天早上,我遇到了类似的需求:如何在Windows命令脚本中将相对路径转换为绝对路径。

以下是技巧:

@echo off

set REL_PATH=..\..\
set ABS_PATH=

rem // Save current directory and change to target directory
pushd %REL_PATH%

rem // Save value of CD variable (current directory)
set ABS_PATH=%CD%

rem // Restore original directory
popd

echo Relative path: %REL_PATH%
echo Maps to path: %ABS_PATH%

2
这真的很有用。是否有诸如此类的批处理文件技巧的良好资源?
丹尼·帕克

8
不要认为“ pushd”后跟“ cd”是不必要的。您可以只执行“ pushd%REL_PATH%”。这将保存当前目录,并一次性切换到REL_PATH。
艾迪·沙利文

1
@DannyParker批处理技术和示例脚本的有用集合是robvanderwoude.com/batchfiles.php。又见stackoverflow.com/questions/245395/...
HFS

6
@EddieSullivan如果更改到目录失败(目录不存在,没有权限...),则pushd命令也会失败,并且实际上不会将值推入目录堆栈。对popd的下一个调用(以及所有后续调用)将弹出错误的值。使用pushd .可以防止此问题。
SvenS

1
请注意,这不适用于文件路径或中间不存在..的不存在路径。对于我来说,这不是一个秀场阻止者,但是对于可重用的解决方案来说,这是一个弱点。
Mihai Danila 2013年

97

这些答案中的大多数似乎对复杂且超级的越野车感到疯狂,这是我的-它适用于任何环境变量(no %CD%PUSHD/ POPDfor /f废话)-只是普通的旧批处理函数。-目录和文件甚至不必存在。

CALL :NORMALIZEPATH "..\..\..\foo\bar.txt"
SET BLAH=%RETVAL%

ECHO "%BLAH%"

:: ========== FUNCTIONS ==========
EXIT /B

:NORMALIZEPATH
  SET RETVAL=%~dpfn1
  EXIT /B

7
这确实是一个干净,有效且直接的解决方案。
拉兹万

3
非常简洁。我现在正在广泛使用这种技术。
PauloFrançaLacerda '18

为什么是%〜dpfn1而不是%〜f1?是否有某种解决方法?%〜f1至少在Windows 7和Windows 10上对我有效。
il--ya

1
@ il--ya似乎%~f1只是它的缩写%~dpfn1-所以,请随意使用%~f1。🙂-长格式~表示删除引号,d表示驱动器,p表示路径,n仅是不fn带扩展名的文件名,而是表示带扩展名的文件名。所述1装置的第一个参数。-(我相信这种格式早于Windows7。)
BrainSlugs83 '19

@ BrainSlugs83这些变量自XP以来可用(在cmd.exe中),但自那时以来,它们的使用没有改变:%〜f1-将%1扩展为完全限定的路径名​​;%〜d1-仅将%1扩展为驱动器号;%〜p1-仅将%1扩展到路径;%〜n1-仅将%1扩展为文件名;%〜x1-仅将%1扩展为文件扩展名。%〜fn1没有特殊情况,它不会产生名称和扩展名,将f,switch组合在一起时,它们仅优先于d,p,n和x。似乎没有充分的理由添加d,p和n。
il--ya

35

无需让另一个批处理文件将参数传递给(和使用参数运算符),您可以使用FOR /F

FOR /F %%i IN ("..\relativePath") DO echo absolute path: %%~fi

其中iin %%~fi是在定义的变量/F %%i。例如。如果将其更改为,/F %%a则最后一部分将是%%~fa

要在命令提示符处(而不是在批处理文件中)执行相同的操作,请替换%%%...


不更改目录很重要,因为它使配方变得健壮,因为该cd命令不必更改%CD%环境变量(例如,如果您在驱动器上D:并且目标路径在C:
jez

@jez为此,您可以使用cd /D D:\on.other.drive
塞巴斯蒂安

FOR /F %%i IN ("..\relativePath") DO echo absolute path: %%~fi如果..\relativePath包含空格,则将失败
Sebastian

1
当我删除/ F标志并使用%%〜dpfnI时,它起作用了。请注意,for /?建议为变量名使用大写字母,以避免与替换参数混淆
Sebastian

1
@SebastianGodelet删除/F将不适用于不存在的路径,它将解析通配符。如果您想要的话,很好,但是如果您想要的功能/F,那么您只需要使用tokens关键字:FOR /F "tokens=*" %%I IN ("..\relative Path\*") DO echo absolute path: %%~fI
SvenS 2016年

15

这是为了帮助填补Adrien Plisson的答案中的空白(编辑后应立即予以删除;-):

您也可以使用%〜f1获得第一个参数的完全限定路径,但这会根据当前路径给出一条路径,这显然不是您想要的。

不幸的是,我不知道如何将2混合在一起...

一个可以处理%0%1同样:

  • %~dpnx0对于批处理文件本身的标准驱动器+路径+名称+扩展名,
    %~f0也足够了;
  • %~dpnx1对于第一个参数的标准驱动器+路径+名称+扩展名(如果完全是文件名),
    %~f1也就足够了;

%~f1将与您指定第一个参数的方式无关:使用相对路径或绝对路径(如果您在命名时未指定文件的扩展名%1,即使使用%~dpnx1- 也不会添加文件扩展名)。

但是,如果您一开始就不会在命令行中提供完整的路径信息,那么无论如何,您将如何命名另一个驱动器上的文件?

然而%~p0%~n0%~nx0并且%~x0可以派上用场了,你应该关心路径(无驱动器号),文件名(不含扩展名),文件全名以扩展或只有文件扩展名。但是请注意,虽然%~p1and %~n1可以找到第一个参数的路径或名称,%~nx1并且%~x1不会添加+显示扩展名,除非您已经在命令行中使用了它。


问题%~dpnx1在于,它提供了相对于当前目录的参数的全限定路径,而OP则要求了相对于批处理文件所在目录的路径。
艾德里安·普里森

@Adrien Plisson:%~dpnx1给出第一个参数的完全限定路径。根本不相关,也不相对于当前目录。的d是用于驱动器,则p是路径,则n是用于文件名SANS后缀时,x为后缀时,1为第一个参数。-内森的问题是:“有没有办法从包含文件名和/或相对路径的值返回绝对路径?”
Kurt Pfeifle,2010年

绝对路径和相对路径用法以及带有description的来源。感激!
it3xl

10

您也可以为此使用批处理功能:

@echo off
setlocal 

goto MAIN
::-----------------------------------------------
:: "%~f2" get abs path of %~2. 
::"%~fs2" get abs path with short names of %~2.
:setAbsPath
  setlocal
  set __absPath=%~f2
  endlocal && set %1=%__absPath%
  goto :eof
::-----------------------------------------------

:MAIN
call :setAbsPath ABS_PATH ..\
echo %ABS_PATH%

endlocal

9

BrainSlugs83的出色解决方案进行小的改进。通用化以允许在调用中命名输出环境变量。

@echo off
setlocal EnableDelayedExpansion

rem Example input value.
set RelativePath=doc\build

rem Resolve path.
call :ResolvePath AbsolutePath %RelativePath%

rem Output result.
echo %AbsolutePath%

rem End.
exit /b

rem === Functions ===

rem Resolve path to absolute.
rem Param 1: Name of output variable.
rem Param 2: Path to resolve.
rem Return: Resolved absolute path.
:ResolvePath
    set %1=%~dpfn2
    exit /b

如果从C:\project输出运行为:

C:\project\doc\build

4

我没有看到很多解决此问题的方法。一些解决方案使用CD进行目录遍历,而其他解决方案则使用批处理功能。我个人偏爱批处理功能,尤其是DosTips提供的MakeAbsolute功能。

该函数具有一些实际好处,首先是它不会更改您当前的工作目录,其次,甚至不必存在要评估的路径。您也可以在此处找到一些有关如何使用该功能的有用提示。

这是一个示例脚本及其输出:

@echo off

set scriptpath=%~dp0
set siblingfile=sibling.bat
set siblingfolder=sibling\
set fnwsfolder=folder name with spaces\
set descendantfolder=sibling\descendant\
set ancestorfolder=..\..\
set cousinfolder=..\uncle\cousin

call:MakeAbsolute siblingfile      "%scriptpath%"
call:MakeAbsolute siblingfolder    "%scriptpath%"
call:MakeAbsolute fnwsfolder       "%scriptpath%"
call:MakeAbsolute descendantfolder "%scriptpath%"
call:MakeAbsolute ancestorfolder   "%scriptpath%"
call:MakeAbsolute cousinfolder     "%scriptpath%"

echo scriptpath:       %scriptpath%
echo siblingfile:      %siblingfile%
echo siblingfolder:    %siblingfolder%
echo fnwsfolder:       %fnwsfolder%
echo descendantfolder: %descendantfolder%
echo ancestorfolder:   %ancestorfolder%
echo cousinfolder:     %cousinfolder%
GOTO:EOF

::----------------------------------------------------------------------------------
:: Function declarations
:: Handy to read http://www.dostips.com/DtTutoFunctions.php for how dos functions
:: work.
::----------------------------------------------------------------------------------
:MakeAbsolute file base -- makes a file name absolute considering a base path
::                      -- file [in,out] - variable with file name to be converted, or file name itself for result in stdout
::                      -- base [in,opt] - base path, leave blank for current directory
:$created 20060101 :$changed 20080219 :$categories Path
:$source http://www.dostips.com
SETLOCAL ENABLEDELAYEDEXPANSION
set "src=%~1"
if defined %1 set "src=!%~1!"
set "bas=%~2"
if not defined bas set "bas=%cd%"
for /f "tokens=*" %%a in ("%bas%.\%src%") do set "src=%%~fa"
( ENDLOCAL & REM RETURN VALUES
    IF defined %1 (SET %~1=%src%) ELSE ECHO.%src%
)
EXIT /b

并输出:

C:\Users\dayneo\Documents>myscript
scriptpath:       C:\Users\dayneo\Documents\
siblingfile:      C:\Users\dayneo\Documents\sibling.bat
siblingfolder:    C:\Users\dayneo\Documents\sibling\
fnwsfolder:       C:\Users\dayneo\Documents\folder name with spaces\
descendantfolder: C:\Users\dayneo\Documents\sibling\descendant\
ancestorfolder:   C:\Users\
cousinfolder:     C:\Users\dayneo\uncle\cousin

希望对您有所帮助...它肯定对我有所帮助:) PS再次感谢DosTips!你摇滚!


谢谢@ulidtko。我以前没有尝试过反对符号链接。
dayneo

2

您可以将它们串联起来。

SET ABS_PATH=%~dp0 
SET REL_PATH=..\SomeFile.txt
SET COMBINED_PATH=%ABS_PATH%%REL_PATH%

路径中间的\ .. \看起来很奇怪,但是可以正常工作。无需做任何疯狂的事情:)


这样就行了。可以进一步简化为echo %~dp0..\SomeFile.txt
Cowlinator

1

在您的示例中,从Bar \ test.bat,DIR / B / S .. \ somefile.txt将返回完整路径。


这不是一个坏主意……我应该用FOR遍历DIR的结果,然后获取第一个结果吗?
内森·泰勒

我考虑过多个文件的问题。您没有指定倍数是否有问题,所以我同意了。至于FOR循环,我在将“ ../”输入for循环时遇到了麻烦,但是是ymmv。如果无法通过DIR命令执行此操作,则可以将输出重定向到文件并循环遍历。我并不是说提取路径的“标准”方法是不好的,只是我认为我的工作满足了您的要求。两种解决方案都具有“某些功能”,并且都有各自的问题。
乔治·斯科斯科

哦,而且,如果您成功地通过DIR循环,则可以覆盖重定向到文件并获得最后的结果。如果您以可以接受的方式撤消订单,则只会得到第一个结果。如果必须遍历文件,则反转顺序以获得第一个结果(位于最后一个位置)也可能会有所帮助。我建议这样做的原因是,获取和丢弃很多东西可能比实际处理要容易得多。我不知道我的思维方式对您是否有意义。只是把它作为一个想法。
乔治·斯科斯科,2009年

如果您需要规范化文件夹的路径,建议您使用以下解决方案: stackoverflow.com/questions/48764076/…–
uceumern

0

这些天,PowerShell很常见,因此我经常将它用作调用C#的快速方法,因为它具有几乎所有功能:

@echo off
set pathToResolve=%~dp0\..\SomeFile.txt
for /f "delims=" %%a in ('powershell -Command "[System.IO.Path]::GetFullPath( '%projectDirMc%' )"') do @set resolvedPath=%%a

echo Resolved path: %resolvedPath%

它有点慢,但是除非不求助于实际的脚本语言,否则获得的功能很难被击败。


0

stijn的解决方案适用于下的子文件夹C:\Program Files (86)\

@echo off
set projectDirMc=test.txt

for /f "delims=" %%a in ('powershell -Command "[System.IO.Path]::GetFullPath( '%projectDirMc%' )"') do @set resolvedPath=%%a

echo full path:    %resolvedPath%

0

文件查看所有其他答案

目录

随着..被你的相对路径,并假设您目前在D:\Projects\EditorProject

cd .. & cd & cd EditorProject (相对路径)

返回绝对路径,例如

D:\Projects


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.