迷人的發現。
某處DosTips還有傑佈崗位描述如何%0
並像一個調用的子程序中的主腳本與%~f0
工作變種:從內部子程序給出子程序的標籤,但加入像%~f0
作品與改性劑%0
運行腳本路徑,即使使用SHIFT也是如此。
但我不記得jeb的帖子描述主程序(無子程序)中引用與未引用%0
之間的區別。
我在下面擴展了MC ND的測試。我的腳本是c:\test\test.bat
。
@echo off
setlocal
echo(
echo Upon entry:
echo ---------------------------------------------------------
echo %%shift%% : %shift%
echo %%cd%% : %cd%
echo %%0 : %0
echo %%1 : %1
echo %%~d0 : %~d0
echo %%~p0 : %~p0
echo %%~n0 : %~n0
echo %%~x0 : %~x0
echo %%~f0 : %~f0
call echo call %%%%~f0 : %%~f0
echo ---------------------------------------------------------
set "shift=FALSE"
d:
echo(
echo Current directory set to D:\
:top
call :getInfo
:getInfo
echo(
if "%0" equ ":getInfo" (
<nul set /p "=From subroutine "
) else (
<nul set /p "=From main "
)
if "%shift%" equ "TRUE" (echo after SHIFT) else (echo before SHIFT)
echo ---------------------------------------------------------
echo %%shift%% : %shift%
echo %%cd%% : %cd%
echo %%0 : %0
echo %%1 : %1
echo %%~d0 : %~d0
echo %%~p0 : %~p0
echo %%~n0 : %~n0
echo %%~x0 : %~x0
echo %%~f0 : %~f0
call echo call %%%%~f0 : %%~f0
echo ---------------------------------------------------------
if "%0" equ ":getInfo" exit /b
if "%shift%" equ "TRUE" exit /b
shift
set "shift=TRUE"
goto top
下面是使用test
爲命令,test
作爲第一個參數結果:
C:\test>test test
Upon entry:
---------------------------------------------------------
%shift% :
%cd% : C:\test
%0 : test
%1 : test
%~d0 : C:
%~p0 : \test\
%~n0 : test
%~x0 : .bat
%~f0 : C:\test\test.bat
call %~f0 : C:\test\test.bat
---------------------------------------------------------
Current directory set to D:\
From subroutine before SHIFT
---------------------------------------------------------
%shift% : FALSE
%cd% : D:\
%0 : :getInfo
%1 :
%~d0 : C:
%~p0 : \test\
%~n0 : test
%~x0 : .bat
%~f0 : C:\test\test.bat
call %~f0 : C:\test\test.bat
---------------------------------------------------------
From main before SHIFT
---------------------------------------------------------
%shift% : FALSE
%cd% : D:\
%0 : test
%1 : test
%~d0 : C:
%~p0 : \test\
%~n0 : test
%~x0 : .bat
%~f0 : C:\test\test.bat
call %~f0 : C:\test\test.bat
---------------------------------------------------------
From subroutine after SHIFT
---------------------------------------------------------
%shift% : TRUE
%cd% : D:\
%0 : :getInfo
%1 :
%~d0 : C:
%~p0 : \test\
%~n0 : test
%~x0 : .bat
%~f0 : C:\test\test.bat
call %~f0 : C:\test\test.bat
---------------------------------------------------------
From main after SHIFT
---------------------------------------------------------
%shift% : TRUE
%cd% : D:\
%0 : test
%1 :
%~d0 : D:
%~p0 : \
%~n0 : test
%~x0 :
%~f0 : D:\test
call %~f0 : D:\test
---------------------------------------------------------
C:\test>
這裏是使用引用值的結果:
C:\test>"test" "test"
Upon entry:
---------------------------------------------------------
%shift% :
%cd% : C:\test
%0 : "test"
%1 : "test"
%~d0 : C:
%~p0 : \test\
%~n0 : test
%~x0 :
%~f0 : C:\test\test
call %~f0 : C:\test\test
---------------------------------------------------------
Current directory set to D:\
From subroutine before SHIFT
---------------------------------------------------------
%shift% : FALSE
%cd% : D:\
%0 : :getInfo
%1 :
%~d0 : C:
%~p0 : \test\
%~n0 : test
%~x0 : .bat
%~f0 : C:\test\test.bat
call %~f0 : C:\test\test.bat
---------------------------------------------------------
From main before SHIFT
---------------------------------------------------------
%shift% : FALSE
%cd% : D:\
%0 : "test"
%1 : "test"
%~d0 : D:
%~p0 : \
%~n0 : test
%~x0 :
%~f0 : D:\test
call %~f0 : D:\test
---------------------------------------------------------
From subroutine after SHIFT
---------------------------------------------------------
%shift% : TRUE
%cd% : D:\
%0 : :getInfo
%1 :
%~d0 : C:
%~p0 : \test\
%~n0 : test
%~x0 : .bat
%~f0 : C:\test\test.bat
call %~f0 : C:\test\test.bat
---------------------------------------------------------
From main after SHIFT
---------------------------------------------------------
%shift% : TRUE
%cd% : D:\
%0 : "test"
%1 :
%~d0 : D:
%~p0 : \
%~n0 : test
%~x0 :
%~f0 : D:\test
call %~f0 : D:\test
---------------------------------------------------------
C:\test>
我得到相同結果來自XP和Win 7.
一切正常在子例程內預期。
但我無法從主層解釋行爲。在SHIFT之前,未加引號的命令與執行腳本的真實路徑一起工作。但是,引用的命令使用命令行中的字符串代替,並使用當前工作的驅動器和目錄填充缺失的值。然而,在SHIFT之後,無引號和引號的值表現相同,它只是與實際傳遞的參數以及當前工作驅動器和目錄一起工作。
因此,在腳本中的任何位置獲取執行腳本的路徑信息的唯一可靠方法是使用子例程。如果當前驅動器和/或目錄自啓動以來已發生更改,或者存在SHIFT爲%0
,則主值上的值將不正確。非常奇怪。充其量,我會將此歸類爲設計缺陷。最糟糕的是,一個徹頭徹尾的bug。
更新
其實,解決您的代碼最簡單的方法就是使用PUSHD POPD和,但我不認爲那是你真正需要的:-)
pushd R:
popd
我曾經認爲你可以通過在改變工作目錄之前捕獲環境變量中的值來解決%~0
問題。但是如果您的腳本使用括號引號進行調用,但沒有.bat
擴展名,則可能會失敗。如果您只查找驅動器,它可以工作,但路徑,基本名稱,擴展名,大小和時間戳等其他內容可能會失敗。
原來,獲得正確值的唯一方法是使用CALLed子例程。
請注意,還有另一個潛在的問題,可能會出現在模糊的情況下。文件和文件夾名稱均可使用^
和!
。如果在啓用延遲擴展的情況下捕獲這些值,則名稱可能會損壞。延遲擴展通常在批處理文件啓動時被禁用,但可能會在啓用延遲擴展的情況下啓動。您可以在捕獲值之前顯式禁用延遲擴展,但使用函數調用還有另一種選擇。
下面的腳本定義了:currentScript
函數,可以在任何情況下使用,並且保證給出正確的值。您傳入一個變量的名稱以接收該值,並可以傳遞一個修飾符字符串(不帶波浪號)。默認選項是F
(完整路徑,相當於DPNX
)
:currentScript
函數位於底部。腳本的其餘部分是演示和測試功能的測試工具。它將使用函數的結果與直接使用%0
的結果進行對比。
@echo off
setlocal disableDelayedExpansion
set arg0=%0
if "%arg0:~0,1%%arg0:~0,1%" equ """" (set "arg0=Quoted") else set "arg0=Unquoted"
call :header
echo %%~tzf0 = "%~tzf0"
call :currentScript rtn tzf
echo :currentScript result = "%rtn%"
setlocal enableDelayedExpansion
call :header
echo %%~tzf0 = "%~tzf0"
call :currentScript rtn tzf
echo :currentScript result = "!rtn!"
endlocal
d:
call :header
echo %%~tzf0 = "%~tzf0"
call :currentScript rtn tzf
echo :currentScript result = "%rtn%"
setlocal enableDelayedExpansion
call :header
echo %%~tzf0 = "%~tzf0"
call :currentScript rtn tzf
echo :currentScript result = "!rtn!"
exit /b
:header
set "rtn="
setlocal
echo(
echo(
if "!" equ "" (set "delayed=ON") else set "delayed=OFF"
if "%cd%\" equ "%~dp0" (set "cwd=Original") else set "cwd=Modified"
echo %arg0%: %cwd% working directory, Delayed expansion = %delayed%
echo ---------------------------------------------------------------------------
exit /b
:currentScript rtnVar [options]
setlocal
set "notDelayed=!"
setlocal disableDelayedExpansion
set "options=%~2"
if not defined options set "options=f"
call set "rtn=%%~%options%0"
if not defined notDelayed set "rtn=%rtn:^=^^%"
if not defined notDelayed set "rtn=%rtn:!=^!%"
endlocal & endlocal & set "%~1=%rtn%" !
exit /b
這裏有一些測試結果時,我給腳本的test^it!.bat
一個瘋狂的名字。我測試了無引號和引用值。您可以看到:CurrentScript
函數始終有效,但直接擴展%~tzf0
通常會失敗。
C:\test>TEST^^IT!.BAT
Unquoted: Original working directory, Delayed expansion = OFF
---------------------------------------------------------------------------
%~tzf0 = "11/07/2013 08:17 PM 1400 C:\test\test^it!.bat"
:currentScript result = "11/07/2013 08:17 PM 1400 C:\test\test^it!.bat"
Unquoted: Original working directory, Delayed expansion = ON
---------------------------------------------------------------------------
%~tzf0 = "11/07/2013 08:17 PM 1400 C:\test\testit.bat"
:currentScript result = "11/07/2013 08:17 PM 1400 C:\test\test^it!.bat"
Unquoted: Modified working directory, Delayed expansion = OFF
---------------------------------------------------------------------------
%~tzf0 = "11/07/2013 08:17 PM 1400 C:\test\test^it!.bat"
:currentScript result = "11/07/2013 08:17 PM 1400 C:\test\test^it!.bat"
Unquoted: Modified working directory, Delayed expansion = ON
---------------------------------------------------------------------------
%~tzf0 = "11/07/2013 08:17 PM 1400 C:\test\testit.bat"
:currentScript result = "11/07/2013 08:17 PM 1400 C:\test\test^it!.bat"
C:\test>"TEST^IT!.BAT"
Quoted: Original working directory, Delayed expansion = OFF
---------------------------------------------------------------------------
%~tzf0 = "11/07/2013 08:17 PM 1400 C:\test\test^it!.bat"
:currentScript result = "11/07/2013 08:17 PM 1400 C:\test\test^it!.bat"
Quoted: Original working directory, Delayed expansion = ON
---------------------------------------------------------------------------
%~tzf0 = "11/07/2013 08:17 PM 1400 C:\test\testit.bat"
:currentScript result = "11/07/2013 08:17 PM 1400 C:\test\test^it!.bat"
Quoted: Modified working directory, Delayed expansion = OFF
---------------------------------------------------------------------------
%~tzf0 = "D:\TEST^IT!.BAT"
:currentScript result = "11/07/2013 08:17 PM 1400 C:\test\test^it!.bat"
Quoted: Modified working directory, Delayed expansion = ON
---------------------------------------------------------------------------
%~tzf0 = "D:\TESTIT.BAT"
:currentScript result = "11/07/2013 08:17 PM 1400 C:\test\test^it!.bat"
C:\test>"TEST^IT!"
Quoted: Original working directory, Delayed expansion = OFF
---------------------------------------------------------------------------
%~tzf0 = "C:\test\TEST^IT!"
:currentScript result = "11/07/2013 10:43 PM 1397 C:\test\test^it!.bat"
Quoted: Original working directory, Delayed expansion = ON
---------------------------------------------------------------------------
%~tzf0 = "C:\test\TESTIT"
:currentScript result = "11/07/2013 10:43 PM 1397 C:\test\test^it!.bat"
Quoted: Modified working directory, Delayed expansion = OFF
---------------------------------------------------------------------------
%~tzf0 = "D:\TEST^IT!"
:currentScript result = "11/07/2013 10:43 PM 1397 C:\test\test^it!.bat"
Quoted: Modified working directory, Delayed expansion = ON
---------------------------------------------------------------------------
%~tzf0 = "D:\TESTIT"
:currentScript result = "11/07/2013 10:43 PM 1397 C:\test\test^it!.bat"
C:\test>
我也test!.bat
的test^it.bat
名稱,測試和test.bat
,和所有工作正常(未顯示)。
這不只是通話 - 這種情況發生,即使你只需要直接執行批處理文件。如果你把'echo%0%〜d0%〜f0'放在裏面,你會得到''test.bat'S:S:\!DJ DAP \ test.bat'。 –
一個瘋狂的猜測。報價是作爲文件名的一部分。這個allways返回當前驅動器:'C:\> for/f「usebackq」%a in('^「^」^「')do echo%〜 dpfnxa' – npocmaka
我希望沒有投票權的最愛是來自OP。這個問題絕對保證在我的書中有投票權。 – dbenham