Giải quyết đường dẫn tuyệt đối từ đường dẫn tương đối và / hoặc tên tệp


154

Có cách nào trong tập lệnh bó Windows để trả về một đường dẫn tuyệt đối từ một giá trị chứa tên tệp và / hoặc đường dẫn tương đối không?

Được:

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

Tôi cần đường dẫn tuyệt đối liên quan đến tệp bó.

Thí dụ:

  • "somefile.txt" nằm trong "C: \ Foo \"
  • "test.bat" nằm trong "C: \ Foo \ Bar".
  • Người dùng mở một cửa sổ lệnh trong "C: \ Foo" và gọi Bar\test.bat ..\somefile.txt
  • Trong tệp bó "C: \ Foo \ somefile.txt" sẽ được lấy từ %1

1
Con đường tương đối không phải là kết thúc của câu chuyện. Cũng xem xét các liên kết tượng trưng NTFS : rất có thể bạn cũng sẽ cần một tương tự realpathđể chuẩn hóa đường dẫn mạnh mẽ.
ulidtko

Có lẽ bạn không cần một con đường chính xác nào cả! Bạn chỉ có thể thêm một đường dẫn cơ sở: SET FilePath=%CD%\%1để nó có thể như thế C:\Foo\Bar\..\..\some\other\dir\file.txt. Các chương trình dường như hiểu một con đường phức tạp như vậy.
Fr0sT

Rất nhiều câu trả lời là điên rồ vì phức tạp, hoặc chỉ là lỗi đơn giản - nhưng, đây thực sự là một điều khá dễ thực hiện theo lô, hãy xem câu trả lời của tôi dưới đây .
BrainSlugs83

Câu trả lời:


158

Trong các tệp bó, như trong các chương trình C tiêu chuẩn, đối số 0 chứa đường dẫn đến tập lệnh hiện đang thực thi. Bạn có thể sử dụng %~dp0để chỉ lấy phần đường dẫn của đối số 0 (là tập lệnh hiện tại) - đường dẫn này luôn là đường dẫn đủ điều kiện.

Bạn cũng có thể có được đường dẫn đủ điều kiện của đối số đầu tiên của mình bằng cách sử dụng %~f1, nhưng điều này đưa ra một đường dẫn theo thư mục làm việc hiện tại, đây rõ ràng không phải là điều bạn muốn.

Cá nhân, tôi thường sử dụng %~dp0%~1thành ngữ trong tệp bó của mình, nó diễn giải đối số đầu tiên liên quan đến đường dẫn của lô thực thi. Mặc dù vậy, nó có một thiếu sót: nó thất bại thảm hại nếu đối số đầu tiên đủ điều kiện.

Nếu bạn cần hỗ trợ cả hai đường dẫn tương đối tuyệt đối, bạn có thể sử dụng giải pháp của Frédéric Ménez : tạm thời thay đổi thư mục làm việc hiện tại.

Đây là một ví dụ sẽ chứng minh từng kỹ thuật sau:

@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

Nếu bạn lưu cái này dưới dạng c: \ temp \ example.bat và chạy nó từ c: \ Users \ Public dưới dạng

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

... bạn sẽ quan sát đầu ra sau:

%~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"

tài liệu cho bộ sửa đổi được phép trên một đối số bó có thể được tìm thấy ở đây: https://docs.microsoft.com/en-us/windows-server/adftime/windows-commands/call


4
Bạn có thể xử lý %0%1tương tự: %~dpnx0đối với đường dẫn đủ điều kiện + tên của chính tệp đó, %~dpnx1cho đường dẫn đủ điều kiện + tên của đối số đầu tiên [nếu đó hoàn toàn là tên tệp]. (Nhưng làm thế nào trên trái đất bạn sẽ đặt tên một tệp trên một ổ đĩa khác nếu bạn sẽ không cung cấp thông tin đường dẫn đầy đủ đó trên dòng lệnh?)
Kurt Pfeifle

Ví dụ này phức tạp hơn nhiều so với câu trả lời @ frédéric-ménez
xuyên suốt

3
Điều này thất bại trên symlink NTFS.
ulidtko

@KurtPfeifle d:\foo\..\bar\xyz.txtvẫn có thể được chuẩn hóa. Tôi khuyên bạn nên sử dụng phương pháp này với câu trả lời của @ axel-heider bên dưới (sử dụng chương trình con theo đợt - sau đó bạn có thể thực hiện trên bất kỳ biến nào, không chỉ là biến được đánh số).
BrainSlugs83

@ulidtko bạn có thể giải thích? Cái gì thất bại? - Bạn đang mong đợi điều gì xảy ra? - Bạn đang mong đợi để giải quyết thư mục gốc? (Bởi vì nếu nó đã làm điều đó , tôi sẽ gọi đó là một thất bại - đó là điểm có điểm liên kết ở vị trí đầu tiên ...)
BrainSlugs83

151

Tôi đã gặp một nhu cầu tương tự sáng nay: làm thế nào để chuyển đổi một đường dẫn tương đối thành một đường dẫn tuyệt đối bên trong một tập lệnh của Windows.

Sau đây đã thực hiện các mẹo:

@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
Điều này thực sự hữu ích. Có bất kỳ tài nguyên tốt cho thủ thuật tập tin hàng loạt như thế này?
Daniel Parker

8
Đừng nghĩ rằng cần phải có "đẩy" theo sau là "cd". Bạn chỉ có thể thực hiện "đẩy% REL_PATH%". Điều đó sẽ lưu thư mục hiện tại và chuyển sang REL_PATH trong một lần.
Eddie Sullivan

1
@DannyParker Một bộ sưu tập hữu ích các kỹ thuật Batch và các kịch bản ví dụ là robvanderwoude.com/batchfiles.php . Xem thêm stackoverflow.com/questions/245395/
Mạnh

6
@EddieSullivan Nếu thay đổi thư mục không thành công (thư mục không tồn tại, không có quyền ...), lệnh Pushd cũng thất bại và sẽ không thực sự đẩy một giá trị lên ngăn xếp thư mục. Cuộc gọi tiếp theo (và tất cả các cuộc gọi liên tiếp) đến popd sẽ bật sai giá trị. Sử dụng pushd .ngăn chặn vấn đề này.
SvenS

1
Lưu ý rằng điều này sẽ không hoạt động trên các đường dẫn là tệp hoặc trên các đường dẫn không tồn tại nơi có một .. ở đâu đó ở giữa. Nó không phải là một điểm dừng chương trình đối với tôi, nhưng nó là một điểm yếu của một giải pháp tái sử dụng.
Mihai Danila

97

Hầu hết các câu trả lời này có vẻ điên rồ vì lỗi phức tạp và siêu lỗi, đây là của tôi - nó hoạt động trên bất kỳ biến môi trường nào, không %CD%hoặc PUSHD/ POPDhoặc for /fvô nghĩa - chỉ là các hàm lô cũ đơn giản. - Thư mục & tập tin thậm chí không tồn tại.

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

ECHO "%BLAH%"

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

:NORMALIZEPATH
  SET RETVAL=%~dpfn1
  EXIT /B

7
Đây thực sự là một giải pháp sạch sẽ, làm việc và đơn giản.
Razvan

3
Rất súc tích. Tôi đang sử dụng techinique này ngay bây giờ, rộng rãi.
Paulo França Lacerda

Tại sao lại là% ~ dpfn1 và không chỉ đơn giản là% ~ f1? Đây có phải là một cách giải quyết? % ~ f1 hoạt động tốt với tôi trên windows 7 và windows 10 ít nhất.
il - ya

1
@ il - ya có vẻ như %~f1chỉ là viết tắt của %~dpfn1- vì vậy, hãy sử dụng %~f1thay thế. - trong định dạng dài ~có nghĩa là xóa dấu ngoặc kép, dcó nghĩa là ổ đĩa, pcó nghĩa là đường dẫn, nmột mình sẽ là tên tệp không có phần mở rộng, nhưng fncó nghĩa là tên tệp có phần mở rộng. các 1nghĩa đối số đầu tiên. - (Tôi tin định dạng này có trước Windows 7.)
BrainSlugs83

@ BrainSlugs83 các biến này có sẵn kể từ XP (trong cmd.exe), nhưng việc sử dụng chúng không thay đổi kể từ đó:% ~ f1 - mở rộng% 1 thành tên đường dẫn đủ điều kiện; % ~ d1 - chỉ mở rộng% 1 thành ký tự ổ đĩa; % ~ p1 - chỉ mở rộng% 1 thành một đường dẫn; % ~ n1 - chỉ mở rộng% 1 thành tên tệp; % ~ x1 - chỉ mở rộng% 1 thành phần mở rộng tệp. Không có trường hợp đặc biệt nào cho% ~ fn1, nó không tạo ra tên + phần mở rộng, công tắc f chỉ đơn giản được ưu tiên hơn d, p, n và x khi chúng được kết hợp. Dường như không có lý do chính đáng để thêm d, p và n.
il - ya

35

Không cần phải có một tệp bó khác để truyền đối số cho (và sử dụng các toán tử đối số), bạn có thể sử dụng FOR /F:

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

trong đó iin %%~filà biến được định nghĩa tại /F %%i. ví dụ. nếu bạn thay đổi nó thành /F %%aphần cuối cùng sẽ là %%~fa.

Để làm điều tương tự ngay tại dấu nhắc lệnh (chứ không phải trong tệp bó) thay thế %%bằng %...


Không thay đổi thư mục rất quan trọng, vì nó làm cho công thức trở nên mạnh mẽ với thực tế là cdlệnh không nhất thiết phải thay đổi %CD%biến môi trường (ví dụ: nếu bạn đang lái xe D:và đường dẫn đích của bạn được bật C:)
jez

@jez Cho rằng bạn có thể sử dụngcd /D D:\on.other.drive
Sebastian

FOR /F %%i IN ("..\relativePath") DO echo absolute path: %%~fisẽ thất bại nếu ..\relativePathkhông gian chứa
Sebastian

1
Nó hoạt động khi tôi xóa cờ / F và sử dụng %% ~ dpfnI. Lưu ý rằng for /?đề xuất sử dụng chữ in hoa cho tên biến, để tránh nhầm lẫn với các tham số thay thế
Sebastian

1
Loại bỏ @SebastianGodelet /Fsẽ không hoạt động trên các đường dẫn không tồn tại và nó sẽ giải quyết các ký tự đại diện. Nếu bạn muốn điều đó, thật tuyệt, nhưng nếu bạn muốn chức năng của /F, thì bạn chỉ cần sử dụng tokenstừ khóa:FOR /F "tokens=*" %%I IN ("..\relative Path\*") DO echo absolute path: %%~fI
SvenS

15

Điều này là để giúp lấp đầy những khoảng trống trong câu trả lời của Adrien Plisson (cần được nâng cấp ngay khi anh ta chỉnh sửa nó ;-):

bạn cũng có thể nhận được đường dẫn đủ điều kiện của đối số đầu tiên của mình bằng cách sử dụng% ~ f1, nhưng điều này đưa ra một đường dẫn theo đường dẫn hiện tại, đây rõ ràng không phải là điều bạn muốn.

Thật không may, tôi không biết cách kết hợp cả hai lại với nhau ...

Người ta có thể xử lý %0%1tương tự:

  • %~dpnx0đối với ổ đĩa đủ điều kiện + đường dẫn + tên + phần mở rộng của chính tệp,
    %~f0cũng đủ;
  • %~dpnx1đối với ổ đĩa đủ điều kiện + đường dẫn + tên + phần mở rộng của đối số đầu tiên [nếu đó hoàn toàn là tên tệp],
    %~f1cũng đủ;

%~f1sẽ hoạt động độc lập với cách bạn đã chỉ định đối số đầu tiên của mình: với các đường dẫn tương đối hoặc với các đường dẫn tuyệt đối (nếu bạn không chỉ định phần mở rộng của tệp khi đặt tên %1, thì nó sẽ không được thêm vào, ngay cả khi bạn sử dụng %~dpnx1- tuy nhiên.

Nhưng làm thế nào trên trái đất bạn sẽ đặt tên một tệp trên một ổ đĩa khác nếu bạn không cung cấp thông tin đường dẫn đầy đủ đó trên dòng lệnh ở vị trí đầu tiên?

Tuy nhiên, %~p0, %~n0, %~nx0%~x0có thể có ích, bạn nên quan tâm đến con đường (không có tên ổ đĩa), tên tập tin (không có phần mở rộng), tên tập tin đầy đủ với phần mở rộng hoặc chỉ phần mở rộng tên tập tin của. Nhưng lưu ý, trong khi %~p1%~n1sẽ làm việc để tìm ra đường dẫn hoặc tên của đối số đầu tiên %~nx1%~x1sẽ không thêm + hiển thị phần mở rộng, trừ khi bạn đã sử dụng nó trên dòng lệnh.


vấn đề với %~dpnx1là nó cung cấp đường dẫn đủ điều kiện của đối số so với thư mục hiện tại , trong khi OP muốn đường dẫn liên quan đến thư mục chứa tệp bó .
Adrien Plisson

@Adrien Plisson: %~dpnx1đưa ra đường dẫn đầy đủ của đối số thứ nhất. Không liên quan gì cả, và cũng không liên quan đến thư mục hiện tại. Cái dlà cho ổ đĩa, cái plà cho đường dẫn, cái ntên là hậu tố sans, hậu tố xlà hậu tố, 1là đối số đầu tiên. - Và câu hỏi của Nathan là: "Có cách nào để trả về một đường dẫn tuyệt đối từ một giá trị chứa tên tệp và / hoặc đường dẫn tương đối không?"
Kurt Pfeifle

Cả hai, sử dụng đường dẫn tuyệt đối và tương đối và nguồn với một mô tả . Đánh giá cao!
it3xl

10

Bạn cũng có thể sử dụng các hàm bó cho việc này:

@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

Cải thiện nhỏ cho giải pháp tuyệt vời của BrainSlugs83 . Tổng quát hóa để cho phép đặt tên biến môi trường đầu ra trong cuộc gọi.

@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

Nếu chạy từ C:\projectđầu ra là:

C:\project\doc\build

4

Tôi chưa thấy nhiều giải pháp cho vấn đề này. Một số giải pháp sử dụng truyền tải thư mục bằng CD và các giải pháp khác sử dụng các hàm bó. Sở thích cá nhân của tôi là dành cho các hàm hàng loạt và đặc biệt là hàm MakeAbsolute được cung cấp bởi DosTips.

Hàm này có một số lợi ích thực sự, chủ yếu là nó không thay đổi thư mục làm việc hiện tại của bạn và thứ hai là các đường dẫn được đánh giá thậm chí không phải tồn tại. Bạn cũng có thể tìm thấy một số lời khuyên hữu ích về cách sử dụng chức năng ở đây .

Đây là một kịch bản ví dụ và kết quả đầu ra của nó:

@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

Và đầu ra:

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

Tôi hy vọng điều này sẽ giúp ... Nó chắc chắn đã giúp tôi :) PS Cảm ơn một lần nữa với DosTips! Bạn đá!


cảm ơn @ulidtko. Tôi đã không cố gắng chống lại các liên kết tượng trưng trước đây.
dayneo

2

Bạn chỉ có thể nối chúng.

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

nó trông có vẻ kỳ lạ với \ .. \ ở giữa đường dẫn của bạn nhưng nó hoạt động. Không cần làm gì điên cả :)


Điều này chỉ hoạt động. Có thể được đơn giản hóa hơn nữaecho %~dp0..\SomeFile.txt
cowlinator

1

Trong ví dụ của bạn, từ Bar \ test.bat, DIR / B / S .. \ somefile.txt sẽ trả về đường dẫn đầy đủ.


Đó không phải là một ý tưởng tồi ... tôi có nên lặp lại kết quả của TRỰC TIẾP bằng FOR và chỉ lấy kết quả đầu tiên không?
Nathan Taylor

Tôi nghĩ về vấn đề với nhiều tập tin. Bạn đã không xác định liệu bội số có phải là vấn đề hay không, vì vậy tôi đã giải quyết nó. Đối với vòng lặp FOR, tôi gặp khó khăn khi cho "../" vào vòng lặp for, nhưng ymmv. Nếu bạn không thể thực hiện nó bằng lệnh DIR, bạn có thể chuyển hướng đầu ra sang một tệp và lặp qua đó. Tôi không nói rằng cách trích xuất 'tiêu chuẩn' là không tốt, chỉ là tôi nghĩ rằng tôi đã làm nhiều hơn những gì bạn yêu cầu. Cả hai giải pháp đều làm "một cái gì đó" và cả hai đều có vấn đề riêng.
George Sisco

Oh..và, nếu bạn lặp qua DIR thành công, bạn có thể ghi đè chuyển hướng vào một tệp và nhận kết quả cuối cùng. Nếu bạn đảo ngược thứ tự theo cách có thể chấp nhận được với bạn, bạn có thể nhận được kết quả đầu tiên. Nếu bạn phải lặp qua tệp, đảo ngược thứ tự để đạt được kết quả đầu tiên ở vị trí cuối cùng cũng có thể giúp ích. Lý do tôi đề nghị là có thể dễ dàng lấy và loại bỏ nhiều thứ hơn là thực sự giải quyết chúng. Tôi không biết cách suy nghĩ của tôi có ý nghĩa với bạn. Chỉ cần đặt nó ra như là một ý tưởng.
George Sisco

Nếu bạn cần bình thường hóa một đường dẫn đến một thư mục, tôi đề xuất giải pháp này: stackoverflow.com/questions/48764076/
mẹo

0

PowerShell khá phổ biến hiện nay vì vậy tôi sử dụng nó thường xuyên như một cách nhanh chóng để gọi C # vì nó có chức năng cho mọi thứ khá nhiều:

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

Hơi chậm một chút, nhưng chức năng thu được rất khó để đánh bại trừ khi không dùng đến ngôn ngữ kịch bản thực tế.


0

giải pháp của stijn hoạt động với các thư mục con bên dưới 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

Tập tin Xem tất cả các câu trả lời khác

Thư mục

Với ..đường dẫn tương đối của bạn và giả sử bạn hiện đang ở D:\Projects\EditorProject:

cd .. & cd & cd EditorProject (đường dẫn tương đối)

trả về đường dẫn tuyệt đối, vd

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.