私はWindows 10 Enterprise x64を使用しています。最も内側のレベルにBATファイルがある次のディレクトリ階層があります。
C:\
dir\
my files\
run.bat
BATファイルには次の行が含まれています。
@pushd %~dp0
@echo %~dp0
@popd
(%~dp0
の意味と使用法は、ヘルプトピックfor /?
および この回答 で説明されています)
現在のディレクトリがC:\dir\my files
であるコマンドプロンプトからBATファイルを実行すると、非常に妥当な結果が得られます。
C:\dir\my files>run.bat
C:\dir\my files\
しかし、親ディレクトリC:\dir
から呼び出すと、次のようになります。
C:\dir>"my files"\run.bat
C:\dir\my files\my files"\
え?最も内側のディレクトリ名が重複していて、最後にいくつかの迷子"\
があることに注意してください。別の方法で試してみましょう。
C:\dir>"my files\run.bat"
C:\dir\my files\my files\
漂遊文字はなくなりましたが、ディレクトリ名はまだ重複しています。これの説明は何ですか?どのディレクトリから呼び出されても同じ出力が得られるようにBATファイルを変更するにはどうすればよいですか?
もちろん、私の実際のシナリオは、この単純化されたバージョンよりも複雑です。 %~dp0
の値は、他の文字列と連結され、環境変数に割り当てられ、引数として他のスクリプトなどに渡されます。
これは cmd.exe内の既知のバグ/設計上の欠陥 --%~dp0
であり、バッチスクリプトへのパスが引用されている場合、バリアントは間違った結果をもたらす可能性があります。
回避策があります。 CALLedサブルーチン内から値を確実に取得できます(~d
、~f
などの修飾子を少なくとも1つ使用する必要があります。使用しない場合は、サブルーチン:label
を取得します)
@echo off
setlocal
pushd %~dp0
echo From main fails: "%~dp0"
call :test
popd
exit /b
:test
echo From subroutine OK: "%~dp0"
-サンプル出力-
d:\dir>"my files\test.bat"
From main fails: "d:\dir\my files\my files\"
From subroutine OK: "d:\dir\my files\"
回避策として、ディレクトリを前もって保存します。
set "dir=%~dp0"
それの訳は %0
は実際には呼び出されたパス(argv
arg 0など)であるため、例では"my files"\run.bat
または"my files\run.bat"
。
あなたがするとき%~dp0
、cmd.exeはフルパスを構築しています現在のディレクトリに対してそしてあなたが要求した部分を抽出します。
pushd
の後、「フルパス」(%~f0
)は次のいずれかになります:
C:\dir\my files\my files\run.bat
C:\dir\my files\my files"\run.bat
...次に、ファイル名を削除して結果を取得します。