解析包含=的命令行参数

时间:2011-08-30 17:20:33

标签: batch-file

使用批处理文件多年后,我惊讶地发现等号'='被认为是一个参数分隔符。

鉴于此测试脚本:

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文件中使用一些args,然后将其余的传递给java应用程序。所以我的第一次尝试是shift,然后重建args列表(因为%*不受shift的影响)。它看起来像这样,就在我发现问题的时候:

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

下一步是不再使用shift,而是通过从%*一次删除一个字符直到遇到空格来手动实现移位:

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应用程序本身的args?答案是我希望能够传递像-Xmx这样的JVM选项,这些选项必须在调用java之前进行处理。

5 个答案:

答案 0 :(得分: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:batchlabel具有相同的参数解析问题,因此无法执行call :mylabel %*然后使用%1!

如果你真的需要%~dp1处理,你可以使用WSH/batch hybrid hack:

@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

答案 1 :(得分:2)

我只回答这个问题:&gt;&gt; 为什么会这样,以及如何避免?

我的建议:使用更好的语言。不,我不是在开玩笑。批处理有太多的怪癖/细微差别,如此加上许多其他限制,它只是不值得时间提出丑陋/低效的解决方法。如果您运行的是Windows 7及更高版本,为什么不尝试使用vbscript甚至PowerShell。这些工具/语言将极大地帮助您完成日常编程/管理任务。作为vbscript如何能够正确处理这样一个问题的一个例子:

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

请注意,它会正确处理参数。

答案 2 :(得分: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%"

答案 3 :(得分: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

答案 4 :(得分:0)

同样,我无法解释意外行为,但有一个非常简单的解决方案。只需更改参数引用,然后在调用时引用参数:

test.bat脚本:

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

调用:

test.bat "a=b" c

输出:

arg1: a=b
arg2: c
arg3:

请注意,使用波浪号(〜)引用参数会从参数中删除前导/尾随引号。