172

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

鉴于:

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

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

例子:

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

14 回答 14

173

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

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

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

如果您需要同时支持相对路径绝对路径,您可以使用Frédéric Mé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/en-us/windows-server/administration/windows-commands/call

于 2009-10-29T19:25:33.803 回答
162

I came across a similar need this morning: how to convert a relative path into an absolute path inside a Windows command script.

The following did the trick:

@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%
于 2010-12-20T10:26:10.843 回答
126

Most of these answers seem crazy over complicated and super buggy, here's mine -- it works on any environment variable, no %CD% or PUSHD/POPD, or for /f nonsense -- just plain old batch functions. -- The directory & file don't even have to exist.

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

ECHO "%BLAH%"

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

:NORMALIZEPATH
  SET RETVAL=%~f1
  EXIT /B
于 2015-10-29T02:18:51.383 回答
38

Without having to have another batch file to pass arguments to (and use the argument operators), you can use FOR /F:

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

where the i in %%~fi is the variable defined at /F %%i. eg. if you changed that to /F %%a then the last part would be %%~fa.

To do the same thing right at the command prompt (and not in a batch file) replace %% with %...

于 2012-04-04T20:46:37.813 回答
15

这是为了帮助填补 Adrien Plisson 答案中的空白(他一编辑就应该投票赞成 ;-):

you can also get the fully qualified path of your first argument by using %~f1, but this gives a path according to the current path, which is obviously not what you want.

unfortunately, i don't know how to mix the 2 together...

One can handle %0 and %1 likewise:

  • %~dpnx0 for fully qualified drive+path+name+extension of the batchfile itself,
    %~f0 also suffices;
  • %~dpnx1 for fully qualified drive+path+name+extension of its first argument [if that's a filename at all],
    %~f1 also suffices;

%~f1 will work independent of how you did specify your first argument: with relative paths or with absolute paths (if you don't specify the file's extension when naming %1, it will not be added, even if you use %~dpnx1 -- however.

But how on earth would you name a file on a different drive anyway if you wouldn't give that full path info on the commandline in the first place?

However, %~p0, %~n0, %~nx0 and %~x0 may come in handy, should you be interested in path (without driveletter), filename (without extension), full filename with extension or filename's extension only. But note, while %~p1 and %~n1 will work to find out the path or name of the first argument, %~nx1 and %~x1 will not add+show the extension, unless you used it on the commandline already.

于 2010-08-13T20:33:11.680 回答
12

You can also use batch functions for this:

@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
于 2011-07-06T10:51:38.440 回答
11

Small improvement to BrainSlugs83's excellent solution. Generalized to allow naming the output environment variable in the call.

@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

If run from C:\project output is:

C:\project\doc\build
于 2017-10-07T11:32:40.237 回答
5

I have not seen many solutions to this problem. Some solutions make use of directory traversal using CD and others make use of batch functions. My personal preference has been for batch functions and in particular, the MakeAbsolute function as provided by DosTips.

The function has some real benefits, primarily that it does not change your current working directory and secondly that the paths being evaluated don't even have to exist. You can find some helpful tips on how to use the function here too.

Here is an example script and its outputs:

@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

And the output:

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

I hope this helps... It sure helped me :) P.S. Thanks again to DosTips! You rock!

于 2012-02-03T13:53:14.553 回答
2

You can just concatenate them.

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

it looks odd with \..\ in the middle of your path but it works. No need to do anything crazy :)

于 2012-11-28T00:28:15.147 回答
1

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

于 2009-10-29T19:17:09.307 回答
0

PowerShell is pretty common these days so I use it often as a quick way to invoke C# since that has functions for pretty much everything:

@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%

It's a bit slow, but the functionality gained is hard to beat unless without resorting to an actual scripting language.

于 2013-07-07T10:35:15.383 回答
0

stijn's solution works with subfolders under 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%
于 2013-11-15T15:06:19.993 回答
0

Files See all other answers

Directories

With .. being your relative path, and assuming you are currently in D:\Projects\EditorProject:

cd .. & cd & cd EditorProject (the relative path)

returns absolute path e.g.

D:\Projects

于 2015-08-03T08:49:20.817 回答
0
SET CD=%~DP0

SET REL_PATH=%CD%..\..\build\

call :ABSOLUTE_PATH    ABS_PATH   %REL_PATH%

ECHO %REL_PATH%

ECHO %ABS_PATH%

pause

exit /b

:ABSOLUTE_PATH

SET %1=%~f2

exit /b
于 2015-09-24T15:43:14.520 回答