代码之家  ›  专栏  ›  技术社区  ›  Nathan Taylor

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

  •  137
  • Nathan Taylor  · 技术社区  · 15 年前

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

    鉴于:

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

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

    例子:

    • “somefile.txt”位于“c:\foo\”
    • “test.bat”位于“c:\foo\bar”中。
    • 用户在“c:\foo”中打开命令窗口并调用 Bar\test.bat ..\somefile.txt
    • 在批处理文件中,“c:\foo\somefile.txt”将从 %1
    14 回复  |  直到 7 年前
        1
  •  145
  •   Community CDub    7 年前

    在批处理文件中,与标准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运行为

    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"
    
        2
  •  135
  •   Peter Mortensen icecrime    10 年前

    今天早上我遇到了一个类似的需求:如何在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%
    
        3
  •  64
  •   BrainSlugs83    9 年前

    这些答案中的大多数似乎都是对复杂和超级马车的疯狂,这是我的——它适用于任何环境变量,不是 %CD% PUSHD / POPD for /f 胡说——只是简单的旧批处理函数。--目录文件甚至不必存在。

    CALL :NORMALIZEPATH "..\..\..\foo\bar.txt"
    SET BLAH=%RETVAL%
    
    ECHO "%BLAH%"
    
    :: ========== FUNCTIONS ==========
    EXIT /B
    
    :NORMALIZEPATH
      SET RETVAL=%~dpfn1
      EXIT /B
    
        4
  •  35
  •   Peter Ritchie    11 年前

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

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

    何处 i 在里面 %%~fi 变量是否定义在 /F %%i . 如果你把它改成 /F %%a 最后一部分是 %%~fa .

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

        5
  •  15
  •   Kurt Pfeifle    14 年前

    这有助于填补阿德里安·普里松(Adrien Plisson)答案中的空白(在他编辑答案后应立即进行上票);—:

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

    不幸的是,我不知道怎么把这两个混在一起…

    一个人能应付 %0 %1 同样地:

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

    %~F1 将独立于您如何指定第一个参数:使用相对路径或绝对路径(如果在命名时不指定文件的扩展名) %1个 ,即使使用 %~dpnx1 --但是。

    但你究竟如何命名一个文件 在另一个驱动器上 无论如何,如果你不首先在命令行上给出完整的路径信息?

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

        6
  •  9
  •   Axel Heider    13 年前

    您还可以使用批处理函数:

    @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
    
        7
  •  6
  •   Lycan    7 年前

    小改进到 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
    
        8
  •  4
  •   dayneo    13 年前

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

    该函数有一些真正的好处,主要是它不会改变当前的工作目录,其次是被评估的路径甚至不必存在。您可以找到一些关于如何使用函数的有用提示 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!你摇滚!

        9
  •  1
  •   George Sisco    15 年前

    在您的示例中,从bar\test.bat,dir/b/s..\somefile.txt将返回完整路径。

        10
  •  1
  •   Kristen    12 年前

    您可以将它们连接起来。

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

    在你的道路中间有\..\看起来很奇怪,但它起作用了。不需要做任何疯狂的事情:)

        11
  •  0
  •   stijn    11 年前

    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%
    

    虽然速度有点慢,但除非不使用实际的脚本语言,否则很难击败所获得的功能。

        12
  •  0
  •   Peter Mortensen icecrime    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%
    
        13
  •  0
  •   Engineer    9 年前

    文件夹 查看所有其他答案

    名录

    .. 作为你的相对路径,假设你现在在 D:\Projects\EditorProject :

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

    返回绝对路径,例如

    D:\Projects

        14
  •  0
  •   Sergei Sachkov    9 年前
    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