【问题标题】:Resolve absolute path from relative path and/or file name从相对路径和/或文件名解析绝对路径
【发布时间】:2010-12-11 09:03:27
【问题描述】:

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

鉴于:

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

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

示例:

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

【问题讨论】:

  • 相对路径并不是故事的结局。还要考虑 NTFS 符号链接:很可能您还需要 realpath 的类似物来实现稳健的路径规范化。
  • 可能您根本不需要确切的路径!您可以只添加一个基本路径:SET FilePath=%CD%\%1,这样它就可以像C:\Foo\Bar\..\..\some\other\dir\file.txt。程序似乎能理解如此复杂的路径。
  • 很多这些答案都过于复杂,或者只是简单的错误——但是,这实际上是一个非常容易批量完成的事情,take a look at my answer below

标签: windows batch-file relative-path absolute-path


【解决方案1】:

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

【讨论】:

  • 这不是一个坏主意...我应该用 FOR 循环遍历 DIR 的结果并获取第一个结果吗?
  • 我想到了多个文件的问题。您没有指定倍数是否有问题,所以我同意了。至于 FOR 循环,我无法将“../”提供给 for 循环,但是 ymmv。如果您无法在 DIR 命令上运行它,您可以将输出重定向到一个文件并循环遍历该文件。我并不是说提取路径的“标准”方法不好,只是我认为我的方法比你要求的更多。两种解决方案都“有所作为”,并且都有自己的一系列问题。
  • 哦..并且,如果您成功循环通过 DIR,您可以覆盖重定向到文件并获得最后一个结果。如果您以您可以接受的方式颠倒顺序,您就可以得到第一个结果。如果您必须遍历文件,则颠倒顺序以获得位于最后位置的第一个结果也可能会有所帮助。我建议这样做的原因是,获得和丢弃很多东西可能比实际处理它们更容易。我不知道我的想法对你是否有意义。只是把它作为一个想法。
  • 如果您需要规范化文件夹的路径,我建议这个解决方案:stackoverflow.com/questions/48764076/…
【解决方案2】:

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

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

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

如果你需要同时支持相对绝对路径,你可以使用Frédéric Ménez's solution:临时改变当前工作目录。

下面是一个演示这些技术的示例:

@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 as 运行它

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

【讨论】:

  • 您可以同样处理%0%1%~dpnx0 用于批处理文件本身的完全限定路径+名称,%~dpnx1 用于其第一个参数的完全限定路径+名称[如果是一个文件名]。 (但是,如果您无论如何都不在命令行上提供完整的路径信息,您将如何在不同的驱动器上命名文件?)
  • 这个例子比@frédéric-ménez 的回答复杂得多
  • 这在 NTFS 符号链接上失败。
  • @KurtPfeifle d:\foo\..\bar\xyz.txt 仍然可以标准化。我建议在下面@axel-heider 的回答中使用这种方法(使用批处理子例程——然后你可以对任何变量执行此操作,而不仅仅是编号变量)。
  • @ulidtko 你能详细说明一下吗?什么失败了? ——你期望发生什么? -- 您是否希望解析到原始文件夹? (因为如果它做到了that,我会称that为失败——这就是首先要有符号链接的意义......)
【解决方案3】:

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

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

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

同样可以处理%0%1

  • %~dpnx0 用于完全限定的驱动器+路径+名称+批处理文件本身的扩展名,
    %~f0 也足够了;
  • %~dpnx1 用于完全限定的驱动器+路径+名称+其第一个参数的扩展名[如果那是文件名的话],
    %~f1 也足够了;

%~f1 将独立于您指定第一个参数的方式:使用相对路径或使用绝对路径(如果您在命名 %1 时未指定文件的扩展名,则不会添加它,即使您使用%~dpnx1 -- 但是。

但是,如果您一开始就不在命令行上提供完整的路径信息,那么您究竟如何在 不同的驱动器上命名文件

但是,%~p0%~n0%~nx0%~x0 可能会派上用场,如果您对路径(不带驱动器号)、文件名(不带扩展名)、带扩展名的完整文件名或仅文件名的扩展名感兴趣.但请注意,虽然%~p1%~n1 可以找出第一个参数的路径或名称,但%~nx1%~x1 不会添加+显示扩展名,除非你已经在命令行上使用了它。

【讨论】:

  • %~dpnx1 的问题在于它给出了相对于 当前目录 的参数的完全限定路径,而 OP 想要相对于 批处理文件所在的目录.
  • @Adrien Plisson: %~dpnx1 给出了第一个参数的完全限定路径。根本不是 relative ,也不是相对于当前目录。 d 用于驱动器,p 用于路径,n 用于无后缀的文件名,x 用于后缀,1 用于第一个参数。 -- Nathan 的问题是:“有没有办法从包含文件名和/或相对路径的值返回绝对路径?”
  • 绝对路径和相对路径 usagesource with an description。欣赏!
【解决方案4】:

今天早上我遇到了类似的需求:如何在 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%

【讨论】:

  • 这真的很有用。有没有像这样的批处理文件技巧的好资源?
  • 不要认为有必要让“pushd”后跟“cd”。您可以执行“pushd %REL_PATH%”。这将保存当前目录并一次性切换到 REL_PATH。
  • @DannyParker 一个有用的批处理技术和示例脚本集合是robvanderwoude.com/batchfiles.php。另见stackoverflow.com/questions/245395/…
  • @EddieSullivan 如果切换到目录失败(目录不存在,没有权限...),pushd 命令也会失败,并且实际上不会将值推送到目录堆栈。对 popd 的下一次调用(以及所有连续调用)将弹出错误的值。使用pushd . 可以防止此问题。
  • 请注意,这不适用于文件路径或中间某处有 .. 的不存在路径。对我来说,这不是一个炫耀,但它是可重用解决方案的一个弱点。
【解决方案5】:

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

@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

【讨论】:

    【解决方案6】:

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

    该函数有一些真正的好处,主要是它不会更改您当前的工作目录,其次是被评估的路径甚至不必存在。您还可以找到一些关于如何使用函数here 的有用提示。

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

    @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
    

    我希望这会有所帮助...它确实帮助了我 :) 附言再次感谢 DosTips!你摇滚!

    【讨论】:

    • 感谢@ulidtko。我以前没有尝试过符号链接。
    【解决方案7】:

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

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

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

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

    【讨论】:

    • 不更改目录很重要,因为它使配方对cd 命令不一定更改%CD% 环境变量(例如,如果您在驱动器@ 987654332@,你的目标路径在C:)
    • @jez 为此你可以使用cd /D D:\on.other.drive
    • 如果..\relativePath 包含空格,FOR /F %%i IN ("..\relativePath") DO echo absolute path: %%~fi 将失败
    • 当我删除 /F 标志并使用 %%~dpfnI 时它起作用了。请注意for /? 建议使用大写字母作为变量名,以避免与替换参数混淆
    • @SebastianGodelet 删除 /F 将不适用于不存在的路径,它将解析通配符。如果你想要,很好,但如果你想要/F 的功能,那么你只需要使用tokens 关键字:FOR /F "tokens=*" %%I IN ("..\relative Path\*") DO echo absolute path: %%~fI
    【解决方案8】:

    你可以把它们串联起来。

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

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

    【讨论】:

    • 这行得通。可以进一步简化为echo %~dp0..\SomeFile.txt
    【解决方案9】:

    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%
    

    它有点慢,但除非不借助实际的脚本语言,否则所获得的功能很难被超越。

    【讨论】:

      【解决方案10】:

      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%
      

      【讨论】:

        【解决方案11】:

        文件查看所有其他答案

        目录

        .. 是您的相对路径,并假设您当前位于 D:\Projects\EditorProject

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

        返回绝对路径,例如

        D:\Projects

        【讨论】:

          【解决方案12】:
          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
          

          【讨论】:

            【解决方案13】:

            这些答案中的大多数似乎对复杂和超级错误感到疯狂,这是我的 - 它适用于任何环境变量,没有 %CD%PUSHD/POPDfor /f 废话 - 只是普通的旧批次职能。 -- 目录和文件甚至不必存在。

            CALL :NORMALIZEPATH "..\..\..\foo\bar.txt"
            SET BLAH=%RETVAL%
            
            ECHO "%BLAH%"
            
            :: ========== FUNCTIONS ==========
            EXIT /B
            
            :NORMALIZEPATH
              SET RETVAL=%~f1
              EXIT /B
            

            【讨论】:

            • 这确实是一个干净、有效且直接的解决方案。
            • 非常简洁。我现在正在广泛使用这种技术。
            • 为什么是 %~dpfn1 而不是简单的 %~f1?这是某种解决方法吗? %~f1 至少在 Windows 7 和 Windows 10 上运行良好。
            • @il--ya 看起来 %~f1 只是 %~dpfn1 的缩写——所以,请随意使用 %~f1。 ? -- 在长格式中~ 表示删除引号,d 表示驱动器,p 表示路径,n 单独是没有扩展名的文件名,但fn 表示有扩展名的文件名。 1 表示第一个参数。 --(我相信这种格式早于 Windows 7。)
            • 啊,你打败了我!根据 nt4 源代码中的注释,文件 nt4\private\windows\cmd\clex.c(日期为 27/08/1997),函数 MSCmdVar(), "// %~fi - 将 %i 扩展为完全限定路径姓名“抱歉打扰你了。我希望我能彻底摆脱这种误解。我在审查一些相当新的代码时遇到了 %~dpfn,我费力地试图理解它应该做什么(请注意,我已经没有多少了),并着手调查它是如何出现的个人仇杀。
            【解决方案14】:

            BrainSlugs83's excellent solution 的小改进。泛化为允许在调用中命名输出环境变量。

            @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
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-06-30
              • 2013-04-17
              • 1970-01-01
              • 2017-04-19
              • 1970-01-01
              • 2010-09-15
              • 2010-10-03
              相关资源
              最近更新 更多