2013-11-05 58 views
3

爲什麼%〜d0返回批處理文件的驅動器號時出現以下故障S:CALL引用批處理文件的名稱時?CMD:CALL引用批處理文件的名稱時%〜d0失敗

S:\!DJ DAP>type test.bat 
R: 
%~d0 

S:\!DJ DAP>call test.bat 

S:\!DJ DAP>R: 

R:\>S: 

S:\!DJ DAP>call "test.bat" 

S:\!DJ DAP>R: 

R:\>R: 

R:\> 

編輯從以下傑裏和MC迴應:這是顯示相同的非呼叫例如:

R:\>s: 

S:\!DJ DAP>type test.bat 
R: 
%~d0 

S:\!DJ DAP>test.bat 

S:\!DJ DAP>R: 

R:\>S: 

S:\!DJ DAP>"test.bat" 

S:\!DJ DAP>R: 

R:\>R: 

R:\> 
+1

這不只是通話 - 這種情況發生,即使你只需要直接執行批處理文件。如果你把'echo%0%〜d0%〜f0'放在裏面,你會得到''test.bat'S:S:\!DJ DAP \ test.bat'。 –

+0

一個瘋狂的猜測。報價是作爲文件名的一部分。這個allways返回當前驅動器:'C:\> for/f「usebackq」%a in('^「^」^「')do echo%〜 dpfnxa' – npocmaka

+0

我希望沒有投票權的最愛是來自OP。這個問題絕對保證在我的書中有投票權。 – dbenham

回答

5

編輯 - npocmaka,你是對的。奇怪。

原始答案已刪除 - 我錯了。

但問題不在於call命令。問題是引號和cmd。

經過測試,它看起來更像是一個錯誤/功能,如何處理文件名以及cmd如何處理api調用中的一些錯誤。

隨着下面的批處理文件(TEST.CMD)

@echo off 
    setlocal enableextensions 

    echo Calling subroutine from drive d: 
    call :getInfo 
    echo. 

    c: 
    echo Calling subroutine from drive c: 
    call :getInfo 
    echo. 

    echo Getting data directly without subroutine 

:getInfo 
    echo --------------------------------------------------------- 
    echo cd : %cd% 
    echo d0 : %~d0 
    echo dp0 : %~dp0 
    echo f0 : %~f0 
    echo --------------------------------------------------------- 
    echo. 
    goto :EOF 

置於d:\ TEMP \ testCMD和在驅動器C的當前目錄:爲C:\用戶,就執行的結果是:

1.- 不調用CMD從目錄報價test.cmd

Calling subroutine from drive d: 
--------------------------------------------------------- 
cd : D:\temp\testCMD 
d0 : D: 
dp0 : D:\temp\testCMD\ 
f0 : D:\temp\testCMD\test.cmd 
--------------------------------------------------------- 


Calling subroutine from drive c: 
--------------------------------------------------------- 
cd : C:\Users 
d0 : D: 
dp0 : D:\temp\testCMD\ 
f0 : D:\temp\testCMD\test.cmd 
--------------------------------------------------------- 


Getting data directly without subroutine 
--------------------------------------------------------- 
cd : C:\Users 
d0 : D: 
dp0 : D:\temp\testCMD\ 
f0 : D:\temp\testCMD\test.cmd 
--------------------------------------------------------- 

結果:一切就OK了。

2:與CMD目錄"test.cmd"報價調用(不,不需要call命令)

Calling subroutine from drive d: 
--------------------------------------------------------- 
cd : D:\temp\testCMD 
d0 : D: 
dp0 : D:\temp\testCMD\ 
f0 : D:\temp\testCMD\test.cmd 
--------------------------------------------------------- 


Calling subroutine from drive c: 
--------------------------------------------------------- 
cd : C:\Users 
d0 : D: 
dp0 : D:\temp\testCMD\ 
f0 : D:\temp\testCMD\test.cmd 
--------------------------------------------------------- 


Getting data directly without subroutine 
--------------------------------------------------------- 
cd : C:\Users 
d0 : C: 
dp0 : C:\Users\ 
f0 : C:\Users\test.cmd 
--------------------------------------------------------- 

結果:失敗來獲得%的正確值〜D0僅當從主執行直接獲得cmd行。子程序調用與預期相同。

所有測試不帶引號的場景都不失敗。使用引號,如果呼叫線路包含驅動器(ej:"d:.\test.cmd"),則所有值均可正確檢索。如果包含在批處理調用中的驅動器(例如:帶有批處理目錄的路徑中爲"test.cmd",或者從D :)的根中得到"\temp\testCMD\test.cmd"),則檢索到的值不正確,但只能從批處理文件中的主線執行。子程序總是得到正確的值。

爲什麼?不知道。但是,當使用procmon跟蹤cmd執行時,在失敗的情況下,當cmd.exe嘗試檢索文件的信息時,將調用C:\Users\test.cmd的QueryDirectory API調用,該調用使用NO SUCH FILE,來回答,但cmd忽略它並繼續執行,錯誤的值

所以,沒有答案,對不起。但我必須「記錄」這一點。房間裏有人大師?

+1

自己測試一下。這真的很奇怪:)。它返回不同的結果,具體取決於是否使用引號或不使用引號。希望批量大師將有一個答案... – npocmaka

+0

感謝MC進行全面分析。 – ChrisJJ

+0

它已經在dbenham的最愛:-D ... – npocmaka

1

"%~dpi"當您列出文件但工作目錄是不同的文件夾或驅動器時也失敗。它顯示工作目錄而不是文件的路徑。

我認爲這裏的解決方案可能是在更改驅動器之前獲取%~d0

4

迷人的發現。

某處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!.battest^it.bat名稱,測試和test.bat,和所有工作正常(未顯示)。

+0

不錯,但我不明白爲什麼你使用''測試''測試''更好地嘗試與'「TEST」「參數1" 」。有些修改器改變了其他一些情況。 – jeb

+0

順便說一句我只描述了函數名的行爲,引用行爲對我來說也是新的, – jeb

+0

謝謝。你能建議我的例子最小的解決方法調整嗎? – ChrisJJ

3

像dbenham:迷人的!

我想這是cmd.exe的一個功能。

主批次上下文中的行情未從%0中除去。
但它們都被一個子程序的調用所剝奪。 當使用兩個以上的引號時,可以實現這一點,當擴展%0時,每一邊只有一個引號將被刪除。

ParamTest.bat

@echo off 
cls 
setlocal 
d: 
echo Main %%0: %~0, %~f0 
echo Main %%1: %~1, %~f1 
call :func %1 
exit /b 

:func 
echo Func %%0: %~0, %~f0 
echo Func %%1: %~1, %~f1 
exit /b 

輸出爲:""""PARAM"test.BAT" ""paramTEST.bAt""

 
Main %0: """PARAM"test.BAT, D:\"""PARAM"test.BAT 
Main %1: "paramTEST.bAt", D:\"paramTEST.bAt" 
Func %0: :func, C:\temp\ParamTest.bat 
Func %1: "paramTEST.bAt", D:\"paramTEST.bAt" 

而且%0似乎保存相關的目錄,你會得到不同的結果%~f0%~f1即使內容似乎是平等的。
但也許路徑只是前綴%0

輸出爲:PARAMtest.BAT paramTEST.bAt

 
Main %0: PARAMtest.BAT, C:\temp\ParamTest.bat 
Main %1: paramTEST.bAt, D:\paramTEST.bAt 
Func %0: :func, C:\temp\ParamTest.bat 
Func %1: paramTEST.bAt, D:\paramTEST.bAt 
相關問題