상대 경로 및 / 또는 파일 이름에서 절대 경로 확인


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

아마 당신은 전혀 정확한 경로가 필요하지 않습니다! 기본 경로를 추가하면 SET FilePath=%CD%\%1다음과 같습니다 C:\Foo\Bar\..\..\some\other\dir\file.txt. 프로그램은 그렇게 복잡한 길을 이해하는 것 같습니다.
Fr0sT

이 답변 중 많은 부분이 복잡하거나 단순한 버그 때문에 미친 짓입니다.하지만 실제로 배치 작업을 수행하는 것은 매우 쉬운 일 입니다. 아래 내 답변을 살펴보십시오 .
BrainSlugs83

답변:


160

표준 C 프로그램에서와 같이 배치 파일에서 인수 0에는 현재 실행중인 스크립트의 경로가 포함됩니다. %~dp00 번째 인수 (현재 스크립트)의 경로 부분 만 가져 오는 데 사용할 수 있습니다. 이 경로는 항상 완전한 경로입니다.

를 사용하여 첫 번째 인수의 완전한 경로를 얻을 수도 %~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) 에서 찾을 수 있습니다.


4
당신은 처리 할 수 %0%1마찬가지로 : %~dpnx0배치 파일 자체의 완전한 경로 + 이름, %~dpnx1첫 번째 인자의 완전한 경로 + 이름 [그 모두에서 파일 이름 인 경우]에 대해. (그러나 어쨌든 명령 줄에 전체 경로 정보를 제공하지 않으면 어떻게 다른 드라이브에서 파일 이름을 지정하겠습니까?)
Kurt Pfeifle

이 예는 @ frédéric-ménez 답변보다 훨씬 더 복잡합니다
srossross

3
NTFS 심볼릭 링크에서 실패합니다.
ulidtko

@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
이것은 정말 유용합니다. 이와 같은 배치 파일 트릭에 대한 좋은 자료가 있습니까?
Danny Parker

8
"pushd"다음에 "cd"가 필요하다고 생각하지 마십시오. "pushd % REL_PATH %"만하면됩니다. 현재 디렉토리를 저장하고 한 번에 REL_PATH로 전환합니다.
Eddie Sullivan

1
@DannyParker Batch 기술과 예제 스크립트의 유용한 모음은 robvanderwoude.com/batchfiles.php 입니다. 참조 stackoverflow.com/questions/245395/...
HFS

6
@EddieSullivan 디렉토리로 변경하는 데 실패하면 (디렉토리가 존재하지 않고 권한이없는 경우) 푸시 된 명령도 실패하며 실제로 디렉토리 스택에 값을 푸시하지 않습니다. 다음 번 통화 (및 모든 연속 통화)에 잘못된 값이 표시됩니다. 사용 pushd .하면이 문제가 방지됩니다.
SvenS

1
이것은 파일 경로 나 중간에 ..가있는 존재하지 않는 경로에서는 작동하지 않습니다. 그것은 나를위한 쇼 스토퍼가 아니지만 재사용 가능한 솔루션에는 약간의 약점입니다.
Mihai Danila 2016 년

97

이 답변의 대부분은 복잡하고 슈퍼 버기보다 미친 것처럼 보입니다. 여기 내 환경 변수, 아니 %CD%또는 PUSHD/ POPD또는 for /f무의미한 평범한 이전 배치 함수에서 작동합니다. -디렉토리와 파일이 없어도됩니다.

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

ECHO "%BLAH%"

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

:NORMALIZEPATH
  SET RETVAL=%~dpfn1
  EXIT /B

7
이것은 실제로 깨끗하고 작동하며 간단한 솔루션입니다.
Razvan

3
매우 간결합니다. 나는 현재이 기술을 널리 사용하고 있습니다.
Paulo França Lacerda

왜 단순히 ~~ f1이 아닌 % ~ dpfn1입니까? 일종의 해결 방법입니까? % ~ f1은 적어도 Windows 7 및 Windows 10에서 나에게 적합합니다.
il--ya

1
@ il--ya는 %~f1짧은 것 같습니다 %~dpfn1- %~f1대신 사용하십시오. 🙂-긴 형식 ~은 따옴표를 제거하고 d드라이브를 p의미하고 경로를 의미하며 경로 n만 있으면 확장자가없는 fn파일 이름 이지만 확장자 는 파일 이름을 의미합니다. 는 1첫번째 인자를 의미한다. - (I이 형식은 윈도우 7을 선행 믿는다)
BrainSlugs83

@ BrainSlugs83이 변수는 XP (cmd.exe) 이후에 사용할 수 있지만 그 이후로 그 사용은 변경되지 않았습니다. % ~ f1-% 1을 정규화 된 경로 이름으로 확장합니다. % ~ d1-% 1을 드라이브 문자로만 확장합니다. % ~ p1-% 1을 경로로만 확장합니다. % ~ n1-% 1을 파일 이름으로 만 확장합니다. % ~ x1-% 1을 파일 확장자로만 확장합니다. % ~ fn1의 특별한 경우는 없으며 이름 + 확장명을 생성하지 않으며 f 스위치는 결합 될 때 단순히 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 당신이 사용할 수 있도록cd /D D:\on.other.drive
Sebastian

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

15

이것은 Adrien Plisson의 답변의 격차를 메우는 데 도움이됩니다 (편집자가 그것을 수정하자마자 공감해야합니다).

% ~ f1을 사용하여 첫 번째 인수의 완전한 경로를 얻을 수도 있지만 현재 경로에 따라 경로를 제공합니다.

불행히도, 나는 2를 함께 섞는 방법을 모른다 ...

하나는 처리 할 수 %0%1마찬가지로 :

  • %~dpnx0 배치 파일 자체의 완전한 드라이브 + 경로 + 이름 + 확장
    %~f0 에도 충분합니다.
  • %~dpnx1완전한 인수 인 첫 번째 인자의 drive + path + name + extension (그것이 파일 이름이라면)
    %~f1도 충분합니다;

%~f1상대 경로 또는 절대 경로를 사용하여 첫 번째 인수를 지정 한 방법에 관계없이 작동합니다 (이름을 지정할 때 파일 확장자를 지정하지 않으면 사용 %1하더라도 추가되지 않습니다)%~dpnx1 - .

그러나 어떻게 지구상 에서 다른 드라이브에 파일 이름을 지정 합니까? 어쨌든 명령 줄에 전체 경로 정보를 제공하지 않으면 어쨌든 합니까?

그러나 %~p0, %~n0, %~nx0%~x0확장 또는 유일한 파일 이름의 확장자로, 당신이 (드라이브 문자 제외) 경로에 관심을 가져야, 편리 (확장자가없는) 파일 이름, 전체 파일 이름을 올 수 있습니다. 그러나 참고, 동안 %~p1%~n1첫 번째 인수의 경로 또는 이름을 알아 내기 위해 노력할 것, %~nx1그리고 %~x1추가 + 이미 명령 줄에서 그것을 사용하지 않는 확장자를 표시하지 않습니다.


문제 %~dpnx1현재 디렉토리 에 대해 인수의 완전한 경로를 제공하는 반면 OP는 배치 파일이있는 디렉토리에 상대적인 경로를 원한다는 것 입니다.
Adrien Plisson

@Adrien Plisson : %~dpnx1첫 번째 인수의 정규화 된 경로를 제공합니다. 전혀 상대적이 아니며 현재 디렉토리와 관련이 없습니다. 는 d(가), 드라이브 인 p(가), 경로입니다 n(가), 파일 이름 산세 접미사입니다 x는 접미사위한 1첫 번째 인수를위한 것입니다. -Nathan의 질문은 "파일 이름 및 / 또는 상대 경로가 포함 된 값에서 절대 경로를 반환 할 방법이 있습니까?"입니다.
커트 파이 플

설명과 함께 절대 및 상대 경로 사용법소스 . 감사합니다!
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를 사용하여 디렉토리 순회를 사용하고 다른 솔루션은 일괄 처리 기능을 사용합니다. 개인적으로 선호하는 것은 배치 기능, 특히 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의 결과를 반복하고 첫 번째 결과를 가져와야합니까?
Nathan Taylor

여러 파일의 문제에 대해 생각했습니다. 배수가 문제인지 여부를 지정하지 않았으므로 함께 진행했습니다. FOR 루프의 경우 "../"를 for 루프에 공급하는 데 문제가 있지만 ymmv입니다. DIR 명령을 수행 할 수 없으면 출력을 파일로 리디렉션하고이를 반복 할 수 있습니다. 나는 경로를 추출하는 '표준'방법이 나쁘다는 것을 말하는 것이 아니라, 내가 당신이 요구 한 것을 더 많이했다고 생각했을뿐입니다. 두 솔루션 모두 '무언가'를 수행하며 모두 고유 한 문제가 있습니다.
George Sisco

그리고 DIR을 성공적으로 반복하면 파일로의 리디렉션을 덮어 쓰고 마지막 결과를 얻을 수 있습니다. 수용 가능한 방식으로 주문을 취소하면 첫 번째 결과 만 얻을 수 있습니다. 파일을 반복해야하는 경우 마지막 위치에있는 첫 번째 결과를 얻기 위해 순서를 반대로하면 도움이 될 수 있습니다. 내가 제안하는 이유는 실제로 처리하는 것보다 많은 것을 얻고 버리는 것이 더 쉬울 수 있기 때문입니다. 내 생각이 당신에게 의미가 있는지 모르겠습니다. 그냥 아이디어로 생각하면됩니다.
George Sisco

폴더 경로를 정규화해야하는 경우이 솔루션을 제안합니다. 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


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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.