Mengatasi jalur absolut dari jalur relatif dan / atau nama file

155

Apakah ada cara dalam skrip kumpulan Windows untuk mengembalikan jalur absolut dari nilai yang berisi nama file dan / atau jalur relatif?

Diberikan:

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

Saya perlu path absolut relatif terhadap file batch.

Contoh:

  • "somefile.txt" terletak di "C: \ Foo \"
  • "test.bat" terletak di "C: \ Foo \ Bar".
  • Pengguna membuka jendela perintah di "C: \ Foo" dan memanggil Bar\test.bat ..\somefile.txt
  • Dalam file batch "C: \ Foo \ somefile.txt" akan berasal dari %1
Nathan Taylor
sumber
1
Jalur relatif bukanlah akhir dari cerita. Pertimbangkan juga symlinks NTFS : kemungkinan besar Anda juga akan memerlukan analog realpathuntuk normalisasi jalur yang kuat.
ulidtko
Mungkin Anda tidak membutuhkan jalan yang pasti sama sekali! Anda bisa menambahkan path basis: SET FilePath=%CD%\%1sehingga bisa seperti C:\Foo\Bar\..\..\some\other\dir\file.txt. Program tampaknya memahami jalan yang rumit.
Fr0sT
Banyak dari jawaban ini yang gila karena rumit, atau hanya buggy biasa - tetapi, ini sebenarnya hal yang cukup mudah dilakukan dalam batch, lihat jawaban saya di bawah ini .
BrainSlugs83

Jawaban:

160

Dalam file batch, seperti dalam program C standar, argumen 0 berisi path ke skrip yang sedang dieksekusi. Anda bisa menggunakan %~dp0untuk mendapatkan hanya bagian jalur dari argumen ke-0 (yang merupakan skrip saat ini) - jalur ini selalu merupakan jalur yang sepenuhnya memenuhi syarat.

Anda juga bisa mendapatkan lintasan yang memenuhi syarat penuh dari argumen pertama Anda dengan menggunakan %~f1, tetapi ini memberikan lintasan sesuai dengan direktori kerja saat ini, yang jelas bukan yang Anda inginkan.

Secara pribadi, saya sering menggunakan %~dp0%~1idiom dalam file batch saya, yang mengartikan argumen pertama relatif terhadap path dari batch yang dieksekusi. Itu memang memiliki kekurangan: gagal total jika argumen pertama sepenuhnya memenuhi syarat.

Jika Anda perlu mendukung jalur relatif dan absolut, Anda dapat menggunakan solusi Frédéric Ménez : untuk sementara mengubah direktori kerja saat ini.

Berikut adalah contoh yang akan menunjukkan masing-masing teknik ini:

@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

Jika Anda menyimpan ini sebagai c: \ temp \ example.bat dan jalankan dari c: \ Users \ Public as

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

... Anda akan mengamati output berikut:

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

dokumentasi untuk set pengubah yang diizinkan pada argumen batch dapat ditemukan di sini: https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/call

Adrien Plisson
sumber
4
Anda dapat menangani %0dan %1juga: %~dpnx0untuk path + nama sepenuhnya terkualifikasi dari batchfile itu sendiri, %~dpnx1untuk path + nama penuh kualifikasi argumen pertama [jika itu nama file sama sekali]. (Tapi bagaimana Anda bisa memberi nama file pada drive yang berbeda jika Anda tidak akan memberikan info path lengkap pada commandline?)
Kurt Pfeifle
Contoh ini jauh lebih kompleks daripada jawaban @ frédéric-ménez
srossross
3
Ini gagal pada symlinks NTFS.
ulidtko
@KurtPfeifle d:\foo\..\bar\xyz.txtmasih dapat dinormalisasi. Saya sarankan menggunakan pendekatan ini dengan jawaban @ axel-heider di bawah ini (menggunakan subrutin batch - maka Anda dapat melakukannya pada variabel apa pun, bukan hanya variabel bernomor).
BrainSlugs83
@ alttko dapatkah Anda menjelaskan? Apa yang gagal? - Apa yang kamu harapkan terjadi? - Apakah Anda berharap untuk menyelesaikan ke folder asli? (Karena jika hal itu bahwa , saya sebut bahwa kegagalan - itu semacam gunanya memiliki symlink di tempat pertama ...)
BrainSlugs83
151

Saya menemukan kebutuhan serupa pagi ini: bagaimana mengubah jalur relatif menjadi jalur absolut di dalam skrip perintah Windows.

Berikut ini triknya:

@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%
Frédéric Ménez
sumber
2
Ini BENAR-BENAR berguna. Apakah ada sumber daya yang bagus untuk trik file batch seperti ini?
Danny Parker
8
Jangan pikir itu perlu untuk memiliki "pushd" diikuti oleh "cd". Anda bisa melakukan "pushd% REL_PATH%". Itu akan menyimpan direktori saat ini dan beralih ke REL_PATH dalam sekali jalan.
Eddie Sullivan
1
@DannyParker Kumpulan teknik Batch dan contoh skrip yang berguna adalah robvanderwoude.com/batchfiles.php . Lihat juga stackoverflow.com/questions/245395/…
hfs
6
@ EddieSullivan Jika mengubah ke direktori gagal (direktori tidak ada, tidak ada izin ...), perintah pushd juga gagal dan tidak akan benar-benar mendorong nilai ke tumpukan direktori. Panggilan berikutnya (dan semua panggilan berurutan) ke popd akan memunculkan nilai yang salah. Penggunaan pushd .mencegah masalah ini.
SvenS
1
Perhatikan bahwa ini tidak akan berfungsi di jalur yang merupakan file atau di jalur yang tidak ada di mana ada .. di suatu tempat di tengah. Bagi saya itu bukan penghenti acara, tetapi ini merupakan kelemahan bagi solusi yang dapat digunakan kembali.
Mihai Danila
97

Sebagian besar jawaban ini tampak gila karena rumit dan sangat bermasalah, ini milik saya - ini berfungsi pada variabel lingkungan apa pun, tidak ada %CD%atau PUSHD/ POPD, atau for /fomong kosong - hanya fungsi batch lama. - Direktori & file bahkan tidak harus ada.

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

ECHO "%BLAH%"

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

:NORMALIZEPATH
  SET RETVAL=%~f1
  EXIT /B
BrainSlugs83
sumber
7
Ini memang solusi yang bersih, berfungsi, dan langsung.
Razvan
3
Sangat singkat. Saya menggunakan techinique ini sekarang, secara luas.
Paulo França Lacerda
1
Mengapa% ~ dpfn1 dan bukan hanya% ~ f1? Apakah ini semacam solusi? % ~ f1 berfungsi dengan baik untuk saya di windows 7 dan windows 10 setidaknya.
il - ya
1
@ il - ya sepertinya %~f1hanya kependekan dari %~dpfn1- jadi, silakan gunakan %~f1saja. 🙂 - dalam format panjang ~berarti menghapus tanda kutip, dberarti drive, pberarti path, nsendirian akan nama file tanpa ekstensi, tetapi fnberarti nama file dengan ekstensi. yang 1berarti argumen pertama. - (Saya yakin format ini ada sebelum Windows 7.)
BrainSlugs83
1
Ah, Anda mengalahkan saya untuk itu! Menurut komentar dalam kode sumber nt4, file nt4 \ private \ windows \ cmd \ clex.c (tanggal 27/08/1997), fungsi MSCmdVar (), "//% ~ fi - perluas% i ke jalur yang sepenuhnya memenuhi syarat name "Maaf telah mengganggu Anda. Saya berharap bahwa saya bisa sampai pada dasar kesalahpahaman ini. Saya menemukan% ~ dpfn ketika meninjau beberapa kode yang cukup baru, menarik beberapa helai rambut saya mencoba memahami apa yang seharusnya dilakukan (ingat Anda saya tidak memiliki banyak yang tersisa), dan mulai menyelidiki bagaimana kode itu hidup seperti dendam pribadi.
il - ya
35

Tanpa harus memiliki file batch lain untuk meneruskan argumen (dan menggunakan operator argumen), Anda dapat menggunakan FOR /F:

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

di mana iin %%~fiadalah variabel yang didefinisikan pada /F %%i. misalnya. jika Anda mengubahnya /F %%amaka bagian terakhir akan menjadi %%~fa.

Untuk melakukan hal yang sama di prompt perintah (dan bukan dalam file batch) ganti %%dengan %...

Peter Ritchie
sumber
Tidak mengubah direktori itu penting, karena itu membuat resep menjadi kuat dengan fakta bahwa cdperintah tersebut tidak serta-merta mengubah %CD%variabel lingkungan (jika misalnya Anda berada di drive D:dan jalur target Anda aktif C:)
j
@jez Untuk itu Anda bisa menggunakancd /D D:\on.other.drive
Sebastian
1
FOR /F %%i IN ("..\relativePath") DO echo absolute path: %%~fiakan gagal jika ..\relativePathruang berisi
Sebastian
1
Ini berfungsi ketika saya menghapus flag / F dan menggunakan %% ~ dpfnI. Catatan yang for /?menyarankan untuk menggunakan huruf kapital untuk nama variabel, untuk menghindari kebingungan dengan parameter substitusi
Sebastian
1
Menghapus @SebastianGodelet /Ftidak akan berfungsi di jalur yang tidak ada, dan itu akan menyelesaikan karakter wildcard. Jika Anda menginginkannya, hebat, tetapi jika Anda menginginkan fungsionalitasnya /F, maka Anda hanya perlu menggunakan tokenskata kunci:FOR /F "tokens=*" %%I IN ("..\relative Path\*") DO echo absolute path: %%~fI
SvenS
15

Ini untuk membantu mengisi kekosongan dalam jawaban Adrien Plisson (yang harus ditingkatkan segera setelah dia mengeditnya ;-):

Anda juga bisa mendapatkan jalur yang sepenuhnya memenuhi syarat dari argumen pertama Anda dengan menggunakan% ~ f1, tetapi ini memberikan jalur sesuai dengan jalur saat ini, yang jelas bukan yang Anda inginkan.

sayangnya, saya tidak tahu bagaimana cara mencampurkan keduanya ...

Seseorang dapat menangani %0dan %1juga:

  • %~dpnx0untuk drive + path + nama + ekstensi + dari batchfile itu sendiri,
    %~f0juga cukup;
  • %~dpnx1untuk drive + lintasan + nama + ekstensi sepenuhnya argumen pertama [jika itu nama file sama sekali],
    %~f1juga cukup;

%~f1akan bekerja terlepas dari bagaimana Anda menentukan argumen pertama Anda: dengan jalur relatif atau dengan jalur absolut (jika Anda tidak menentukan ekstensi file saat penamaan %1, itu tidak akan ditambahkan, bahkan jika Anda menggunakan %~dpnx1- namun.

Tapi bagaimana pula Anda memberi nama file pada drive yang berbeda jika Anda tidak akan memberikan info path lengkap pada commandline di tempat pertama?

Namun, %~p0, %~n0, %~nx0dan %~x0mungkin berguna, jika Anda tertarik pada jalan (tanpa hurufkandar), nama file (tanpa ekstensi), nama file penuh dengan ekstensi atau ekstensi nama file hanya. Tetapi perhatikan, saat %~p1dan %~n1akan bekerja untuk mengetahui jalur atau nama argumen pertama, %~nx1dan %~x1tidak akan menambahkan + menunjukkan ekstensi, kecuali Anda sudah menggunakannya pada commandline.

Kurt Pfeifle
sumber
masalahnya %~dpnx1adalah bahwa ia memberikan path yang sepenuhnya memenuhi syarat dari argumen relatif ke direktori saat ini , sementara OP menginginkan path relatif ke direktori di mana file batch berada .
Adrien Plisson
1
@Adrien Plisson: %~dpnx1memberikan jalur argumen 1 yang sepenuhnya memenuhi syarat. Tidak relatif sama sekali, dan tidak relatif terhadap dir saat ini juga. Ini duntuk drive, puntuk path, nuntuk nama file sans suffix, xuntuk suffix, 1untuk argumen pertama. - Dan pertanyaan Nathan adalah: "Apakah ada cara untuk mengembalikan jalur absolut dari nilai yang berisi nama file dan / atau jalur relatif?"
Kurt Pfeifle
Baik, penggunaan jalur absolut dan relatif dan sumber dengan deskripsi . Menghargai!
it3xl
10

Anda juga dapat menggunakan fungsi batch untuk ini:

@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
Axel Heider
sumber
9

Perbaikan kecil untuk solusi hebat BrainSlugs83 . Umum untuk memungkinkan penamaan variabel lingkungan keluaran dalam panggilan.

@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

Jika dijalankan dari C:\projectoutput adalah:

C:\project\doc\build

sumber
4

Saya belum melihat banyak solusi untuk masalah ini. Beberapa solusi memanfaatkan traversal direktori menggunakan CD dan lainnya menggunakan fungsi batch. Preferensi pribadi saya adalah untuk fungsi batch dan khususnya, fungsi MakeAbsolute yang disediakan oleh DosTips.

Fungsi ini memiliki beberapa manfaat nyata, terutama karena tidak mengubah direktori kerja Anda saat ini dan kedua bahwa jalur yang dievaluasi bahkan tidak harus ada. Anda dapat menemukan beberapa kiat bermanfaat tentang cara menggunakan fungsi di sini juga.

Berikut ini contoh skrip dan hasilnya:

@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

Dan hasilnya:

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

Saya harap ini membantu ... Ini tentu membantu saya :) PS Terima kasih lagi untuk DosTips! Kamu keren!

siang hari
sumber
terima kasih @ulidtko. Saya belum pernah mencoba melawan symlink sebelumnya.
dayneo
2

Anda bisa menggabungkannya.

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

itu terlihat aneh dengan \ .. \ di tengah jalan Anda tetapi berfungsi. Tidak perlu melakukan hal-hal gila :)

Kristen
sumber
Ini hanya berfungsi. Lebih lanjut dapat disederhanakan keecho %~dp0..\SomeFile.txt
cowlinator
1

Dalam contoh Anda, dari Bar \ test.bat, DIR / B / S .. \ somefile.txt akan mengembalikan path lengkap.

George Sisco
sumber
Itu bukan ide yang buruk ... haruskah saya mengulang hasil DIR dengan FOR dan hanya mengambil hasil pertama?
Nathan Taylor
Saya memikirkan masalah dengan banyak file. Anda tidak menentukan apakah kelipatan merupakan masalah, jadi saya mengikutinya. Adapun loop UNTUK, saya mengalami kesulitan memberi makan "../" ke loop untuk, tapi ymmv. Jika Anda tidak bisa menjalankannya dengan perintah DIR, Anda mungkin mengarahkan output ke file dan mengulanginya. Saya tidak mengatakan cara 'standar' untuk mengekstraksi jalur itu buruk, hanya saja saya pikir cara saya melakukan lebih banyak dari yang Anda minta. Kedua solusi melakukan 'sesuatu', dan mereka berdua memiliki serangkaian masalah mereka sendiri.
George Sisco
Oh..dan, jika Anda berhasil melewati DIR, Anda bisa menimpa redirect ke file dan mendapatkan hasil terakhir. Jika Anda membalik urutan dengan cara yang dapat Anda terima, Anda bisa mendapatkan hasil pertama saja. Jika Anda harus mengulang-ulang file, membalik urutan untuk mendapatkan hasil pertama yang akan berada di posisi terakhir juga dapat membantu. Alasan saya menyarankan bahwa mungkin lebih mudah untuk mendapatkan dan membuang banyak hal daripada benar-benar menghadapinya. Saya tidak tahu apakah cara berpikir saya masuk akal bagi Anda. Hanya menaruhnya di luar sana sebagai ide.
George Sisco
Jika Anda perlu menormalkan path ke folder, saya sarankan solusi ini: stackoverflow.com/questions/48764076/…
uceumern
0

PowerShell sangat umum hari ini, jadi saya sering menggunakannya sebagai cara cepat untuk memanggil C # karena itu memiliki fungsi untuk hampir semua:

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

Agak lambat, tetapi fungsi yang didapat sulit dikalahkan kecuali tanpa menggunakan bahasa skrip yang sebenarnya.

stijn
sumber
0

solusi stijn bekerja dengan subfolder di bawah 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%
Irq Mishell
sumber
0

File Lihat semua jawaban lain

Direktori

Dengan ..menjadi jalur relatif Anda, dan dengan asumsi Anda saat ini berada di D:\Projects\EditorProject:

cd .. & cd & cd EditorProject (jalur relatif)

mengembalikan jalur absolut misalnya

D:\Projects

Insinyur
sumber
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
Sergei Sachkov
sumber