2011-08-30 55 views
9
的cmdline參數

使用批處理文件多年後,我驚訝地發現等號'='被認爲是參數分隔符。分析包含=

鑑於這種測試腳本:

echo arg1: %1 
echo arg2: %2 
echo arg3: %3 

和調用:

test.bat a=b c 

輸出爲:

arg1: a 
arg2: b 
arg3: c 

這是爲什麼和如何能避免?我不希望劇本的用戶解釋這個怪癖並引用「a = b」,這是違反直覺的。

此批處理腳本是在Windows 7

=====編輯=====

運行多一點背景:寫一個bat文件時啓動Java我遇到了這個問題應用。我想在bat文件中使用一些參數,然後將剩下的傳給java應用程序。所以我第一次嘗試做shift,然後重建參數列表(因爲%*不受shift的影響)。它看起來是這樣的,這時候我發現了問題:

rem Rebuild the args, %* does not work after shift 
:args 
if not "%1" == "" (
    set ARGS=!ARGS! %1 
    shift 
    goto args 
) 

下一步是不使用轉向了,但通過一次從%*刪除一個字符,直到一個空間,而實現由指針偏移遇到:

rem Remove the 1st arg if it was the profile 
set ARGS=%* 
if not "%FIRST_ARG%" == "%KNOA_PROFILE%" goto remove_first_done 
:remove_first 
if not defined ARGS goto remove_first_done 
if "%ARGS:~0,1%" == " " goto remove_first_done 
set ARGS=%ARGS:~1% 
goto remove_first 
:remove_first_done 

但這是醜陋的,在某些情況下,我可能還沒有考慮失敗。所以最後我決定編寫一個Java程序來處理參數解析!在我的情況下,這很好,因爲我啓動了一個服務器,額外的Java調用的懲罰是最小的。令人難以置信的是,有時你最終會做什麼。

您可能想知道爲什麼我沒有照顧Java應用程序本身的參數?答案是我希望能夠傳遞像-Xmx這樣的JVM選項,這些選項在調用java之前必須被處理。

+1

逗號,分號和等號是命令參數和FOR集的分隔符。通過分隔符的唯一方法是用引號括起來:test「a = b」c – Aacini

+0

使用'enabledelayedexpansion'並假定第一個參數仍在%1中,相當乾淨的解決方案可能如下所示: 'setlocal enabledelayedexpansion& set args =%*& if'%1「==」%KNOA_PROFILE%「(\t set args =!args:%1 =!)'_split lines on'&',我無法輸入多行comments_ – eremmel

回答

6

我猜它這是否讓/param=data相同/param data

我不知道任何技巧來解決這個愚蠢的(可能是由設計)解析的問題,但我能拿出一個超級難看的解決方法:

@echo off 
setlocal ENABLEEXTENSIONS 
set param=1 
:fixnextparam 
set p= 
((echo "%~1"|find " ")>nul)||(
    call :fixparam %param% "%~1" "%~2" %* 2>nul 
) 
if "%p%"=="" (set "p%param%=%1") else shift 
shift&set /A param=%param% + 1 
if not "%~1"=="" goto fixnextparam 

echo.1=%p1% 
echo.2=%p2% 
echo.3=%p3% 
echo.4=%p4% 
echo.5=%p5% 
goto:EOF 


:fixparam 
set p%1= 
for /F "tokens=4" %%A in ("%*") do (
    if "%%~A"=="%~2=%~3" set p=!&set "p%1=%%A" 
) 
goto:EOF 

當我執行test.cmd foo=bar baz "fizz buzz" w00t我得到:

1=foo=bar 
2=baz 
3="fizz buzz" 
4=w00t 
5= 

的問題,這當然是ÿ你不能做%〜dp1風格的變量擴展。 不可能做call :mylabel %*,然後使用%1,因爲call:batchlabel具有相同的參數解析問題!

如果你真的需要%〜DP1處理您可以使用WSH/batch hybrid黑客:

@if (1==1) @if(1==0) @ELSE 
@echo off 
@SETLOCAL ENABLEEXTENSIONS 
if "%SPECIALPARSE%"=="*%~f0" (
    echo.1=%~1 
    echo.2=%~2 
    echo.3=%~3 
    echo.4=%~4 
    echo.5=%~5 
) else (
    set "SPECIALPARSE=*%~f0" 
    cscript //E:JScript //nologo "%~f0" %* 
) 
@goto :EOF 
@end @ELSE 
w=WScript,wa=w.Arguments,al=wa.length,Sh=w.CreateObject("WScript.Shell"),p=""; 
for(i=0;i<al;++i)p+="\""+wa.Item(i)+"\" "; 
function PipeStream(i,o){for(;!i.AtEndOfStream;)o.Write(i.Read(1))} 
function Exec(cmd,e){ 
    try{ 
     e=Sh.Exec(cmd); 
     while(e.Status==0){ 
      w.Sleep(99); 
      PipeStream(e.StdOut,w.StdOut); 
      PipeStream(e.StdErr,w.StdErr); 
     } 
     return e.ExitCode; 
    }catch(e){return e.number;} 
} 
w.Quit(Exec("\""+WScript.ScriptFullName+"\" "+p)); 
@end 
+0

WOOOOOW - 我以爲我看過所有的WRT批處理文件! –

2

我只回答這個:>>這是爲什麼和如何能避免?

我的建議:使用更好的語言。不,我不是在開玩笑。批處理有太多的怪癖/細微差別,加上很多其他的侷限性,但它不值得花時間提出醜陋/低效率的解決方法。如果您運行的是Windows 7或更高版本,那麼爲什麼不嘗試使用vbscript甚至是powershell。這些工具/語言將極大地幫助您完成日常編程/管理任務。作爲VBScript中如何propely採取這樣一個問題護理的例子:

For i=0 To WScript.Arguments.Count-1 
    WScript.Echo WScript.Arguments(i) 
Next 

輸出:

C:\test>cscript //nologo myscript.vbs a=b c 
a=b 
c 

注意,它需要正確的論據照顧。

+0

vbscripts(WSH)在Win98 +上開箱即可(或在Win95上安裝IE5)。問題當然是你必須在所有命令前加上「cscript // nologo」。 – Anders

+0

不是一個真正的問題,調用「cscript // nologo」就像調用其他命令行工具一樣。例如,'ping -c 1 127.0.0.1' ...此外,如果OP需要,所有需要完成的任務可以寫成一個完整的vbscript程序。沒有必要調用multiiple cscript(當然,不是在你需要一個接一個地調用很多vbs文件的情況下) – ghostdog74

2
@echo off 
setlocal enabledelayedexpansion 

if %1 neq ~ (
set "n=%*" 
for %%a in ("!n: =" "!") do (
set "s=%%a" 
if !s:~0^,2!==^"^" (
    set "s=!s:" "= !" 
    set "s=!s:""="!" 
) 
set "m=!m! !s!" 
) 
%0 ~ !m! 
) 
endlocal 
shift 

echo arg1: %~1 
echo arg1: %~2 
echo arg1: %~dp3 
exit /b 

c:> untested.bat a=b c "%USERPROFILE%" 
+0

+1,好主意,但是用'「hello you *」和= * me'完全失敗 – jeb

1

@jeb:這裏的另一種方法

untested.cmd

@echo off 
setlocal enabledelayedexpansion 

set "param=%*" 
set param="%param: =" "%" 

for /f "delims=" %%a in ('echo %param: =^&echo.%') do (
    set "str=%%a" 
    if !str:~0^,2!==^"^" (
    set "str=!str:^&echo.=!" 
    set "str=!str:"^"= !" 
    set str="!str:~1,-1!" 
) 
    set "m=!m! !str!" 
) 

Call :sub %m% 
endlocal & goto :eof 

:sub 
echo arg1: %~1 
echo arg2: %~2 
echo arg3: %~3 
echo arg4: %~4 
goto :eof 
+0

+1,:-)也很好,但現在失敗了,只需一個簡單的'「one = 1」two = 2' - arg1 ='one 1'(該=缺失)arg2和arg3爲空arg4 ='two = 2 '。我猜想類似於[漂亮的打印,如何拆分..](http://stackoverflow.com/questions/5471556/pretty-print-windows-path-variable-how-to-split-on-in-cmd-外殼/ 5472168#5472168)可以工作。使用解析器分隔未引用的引用文本 – jeb

0

同樣,我無法解釋的異常行爲,但有一個很簡單的解決方案

test.bat的腳本:

echo arg1: %~1 
echo arg2: %~2 
echo arg3: %~3 

調用:

test.bat "a=b" c 

輸出:

arg1: a=b 
arg2: c 
arg3: 

注意只要改變你的論點引用,那麼這樣調用時,引用參數使用波形符號(〜)引用參數會從參數中刪除前導/尾隨引號。

+0

確實,這不是解決方案。從OP'我不希望腳本的用戶解釋這個怪癖並引用「a = b」,這是違反直覺的。「 – jeb

+0

我錯過了那部分,但不是所有的直覺都是平等的。對我而言,閱讀說明是「直觀的」(RTFM,即cmd.exe /?)。它解釋了cmd.exe如何解析命令行引號,但沒有「=」符號解釋。在解決Perl中創建的命令字符串問題時,此問題幫助我識別此問題。複雜的,非直觀的解析器代碼是一個批處理文件解決方案,但它並沒有教導cmd.exe底層設計的基本現實。一旦你明白了,簡單適當的引用解決方案適用於所有調用cmd.exe的腳本shell,包括Batch,Perl,Python等。 – tahoar