相対パスやファイル名から絶対パスを解決する


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 2014

おそらくあなたはまったく正確なパスを必要としません!基本パスを追加するだけで、次のSET FilePath=%CD%\%1ようになりC:\Foo\Bar\..\..\some\other\dir\file.txtます。プログラムはそのような複雑な経路を理解しているようです。
Fr0sT 2015年

これらの答えの多くは、複雑な、または単なるバギーに対して狂っています-しかし、これは実際にはバッチで実行するのはかなり簡単なことです。以下の私の答えを見てください
BrainSlugs83 2016

回答:


160

標準Cプログラムと同様に、バッチファイルでは、引数0に現在実行中のスクリプトへのパスが含まれています。を使用%~dp0して、0番目の引数(現在のスクリプト)のパス部分のみを取得できます。このパスは常に完全修飾パスです。

また、を使用して最初の引数の完全修飾パスを取得することもできます%~f1が、これにより、現在の作業ディレクトリに応じたパスが得られますが、これは明らかに望ましいものではありません。

個人的に%~dp0%~1は、バッチファイルでイディオムをよく使用します。このイディオムは、実行中のバッチのパスに関連する最初の引数を解釈します。ただし、欠点もあります。最初の引数が完全に修飾されていると、惨めに失敗します。

相対パス絶対パスの両方をサポートする必要がある場合は、フレデリックメネスのソリューションを利用できます。現在の作業ディレクトリを一時的に変更します。

これらの手法のそれぞれを示す例を次に示します。

@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その最初の引数の完全修飾パス+名前[それはすべてのファイル名だ場合]のために。(とにかく、コマンドラインで完全なパス情報を提供しない場合は、
いったい

この例は、@frédéric-ménezの回答よりもはるかに複雑です
srossross 2014年

3
これはNTFSシンボリックリンクでは失敗します。
ulidtko 2014

@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
これは本当に便利です。このようなバッチファイルのトリックのための良いリソースはありますか?
ダニーパーカー

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コマンドも失敗し、実際には値がディレクトリスタックにプッシュされません。popdへの次の呼び出し(およびすべての連続した呼び出し)は、誤った値をポップします。を使用pushd .すると、この問題を回避できます。
SvenS 2013

1
これは、ファイルであるパス、または途中に..が存在する存在しないパスでは機能しないことに注意してください。それは私にとってショーストッパーではありませんでしたが、再利用可能なソリューションには少し弱点があります。
Mihai Danila 2013年

97

これらの答えのほとんどは、複雑で非常にバグが多い上に狂っていると思われます。これは私のものです。それは、任意の環境変数、no %CD%またはPUSHD/ POPD、またはfor /f無意味なもので機能します。単なる古いバッチ関数です。-ディレクトリとファイルは存在する必要さえありません。

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

ECHO "%BLAH%"

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

:NORMALIZEPATH
  SET RETVAL=%~f1
  EXIT /B

7
これは確かに、クリーンで実用的で簡単なソリューションです。
Razvan

3
とても簡潔です。私は今、このテクニックを広く使用しています。
パウロ・フランサラセルダ

1
なぜ%〜dpfn1であり、単に%〜f1ではないのですか?何らかの回避策ですか?%〜f1は、少なくともWindows 7およびWindows 10では正常に動作します。
il--ya

1
@ il--ya %~f1は単にの略のように見える%~dpfn1ので、%~f1代わりに自由に使用してください。🙂-長い形式で~は、引用符を削除することを意味し、dドライブ、pパスを意味し、n単独では拡張子のないファイル名になりますが、拡張子のあるファイル名をfn意味します。これ1は最初の引数を意味します。-(この形式はWindows 7よりも
古いものだと思い

1
ああ、あなたは私をそれに打ち負かした!nt4ソースコードのコメントによると、ファイルnt4 \ private \ windows \ cmd \ clex.c(1997年8月27日)、関数MSCmdVar()、 "//%〜fi-%iを完全修飾パスに展開します名前 "ごめんなさいごめんなさい 私はこの誤解の底に到達できることを望んでいました。かなり最近のコードを確認しているときに%〜dpfnに遭遇し、それが何をすべきかを理解するためにいくつかの髪を引っ張って(私は多くを残していないことに注意してください)、それがどのようにして生きているのか調査することに着手しました個人的な復讐。
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

@jezそのために使用できますcd /D D:\on.other.drive
セバスチャン

1
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最初の引数の完全修飾されたドライブ+パス+名前+拡張子の場合(それがファイル名の場合)、これ
    %~f1も十分です。

%~f1最初の引数を指定した方法とは関係なく動作します:相対パスまたは絶対パス%1を使用します%~dpnx1(ただし、の命名時にファイルの拡張子を指定しない場合、使用しても追加されません-。

しかし、そもそもコマンドラインで完全なパス情報を提供しないのであれば、いったいどのようにして別のドライブ上のファイル名前を付けるのでしょうか。

しかし、%~p0%~n0%~nx0そして%~x0便利になることがあり、あなたは(ドライブ文字なし)のパスに関心を持つべき(拡張子なし)、ファイル名、拡張子やファイル名のみの拡張子が付いた完全なファイル名。しかし、ノート、一方、%~p1及び%~n1第一引数のパスや名前を見つけるために働くだろう、%~nx1%~x1あなたはすでにコマンドライン上でそれを使用しない限り、拡張子を追加+表示されません。


の問題%~dpnx1は、現在のディレクトリに関連する引数の完全修飾パスを提供するのに対し、OP はバッチファイルが存在するディレクトリに関連するパスを必要とすることです
Adrien Plisson、2010

1
@Adrien Plisson:%~dpnx1第1引数の完全修飾パスを提供します。ない相対現在のディレクトリからの相対全てではなく、どちらか。d、ドライブ用でp、パスにあるn、ファイル名のsansサフィックスのためであるx、接尾辞のためにある1最初の引数にあります。-そしてネイサンの質問は、「ファイル名や相対パスを含む値から絶対パスを返す方法はありますか?」
カートPfeifle 2010

説明付きの絶対パスと相対パスの使用法ソースの両方。感謝しています!
it3xl 2018

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を使用したディレクトリトラバーサルを利用するソリューションもあれば、バッチ機能を利用するソリューションもあります。私の個人的な好みは、バッチ関数、特にDosTipsによって提供される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
カウリネーター

1

あなたの例では、Bar \ test.batから、DIR / B / S .. \ somefile.txtは完全パスを返します。


それは悪い考えではありません... DIRの結果をFORでループして、最初の結果を取得する必要がありますか?
ネイサンテイラー

複数のファイルの問題について考えました。倍数が問題であるかどうかを指定しなかったので、私はそれを採用しました。FORループについては、「../」をforループに送るのに問題がありますが、ymmvです。DIRコマンドで実行できない場合は、出力をファイルにリダイレクトし、それをループすることができます。パスを抽出する「標準」の方法が悪いと言っているのではありません。私があなたが尋ねたことをもっとやったと思っただけです。どちらのソリューションも「何か」を行い、どちらにも独自の問題があります。
ジョージ・スコ

ああ、それから、DIRをうまくループできたら、ファイルへのリダイレクトを上書きして、最後の結果を得ることができます。許容できる方法で順序を逆にすると、最初の結果だけが得られます。ファイルをループする必要がある場合は、順序を逆にして、最後の位置にある最初の結果を取得することも役立つ場合があります。私が提案する理由は、実際にそれらを処理するよりも、多くのものを取得および破棄する方が簡単かもしれないからです。私の考え方があなたにとって意味のあるものかどうかわかりません。アイデアとしてそこに出すだけです。
ジョージ・スコ

フォルダへのパスを正規化する必要がある場合、私は、このソリューションを提案: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.