分析包含=
使用批处理文件多年后,我惊讶地发现等号'='被认为是参数分隔符。分析包含=
鉴于这种测试脚本:
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之前必须被处理。
我猜它这是否让/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
WOOOOOW - 我以为我看过所有的WRT批处理文件! –
我只回答这个:>>这是为什么和如何能避免?
我的建议:使用更好的语言。不,我不是在开玩笑。批处理有太多的怪癖/细微差别,加上很多其他的局限性,但它不值得花时间提出丑陋/低效率的解决方法。如果您运行的是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
注意,它需要正确的论据照顾。
vbscripts(WSH)在Win98 +上开箱即可(或在Win95上安装IE5)。问题当然是你必须在所有命令前加上“cscript // nologo”。 – Anders
不是一个真正的问题,调用“cscript // nologo”就像调用其他命令行工具一样。例如,'ping -c 1 127.0.0.1' ...此外,如果OP需要,所有需要完成的任务可以写成一个完整的vbscript程序。没有必要调用multiiple cscript(当然,不是在你需要一个接一个地调用很多vbs文件的情况下) – ghostdog74
@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%"
+1,好主意,但是用'“hello you *”和= * me'完全失败 – jeb
@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
+1,:-)也很好,但现在失败了,只需一个简单的'“one = 1”two = 2' - arg1 ='one 1'(该=缺失)arg2和arg3为空arg4 ='two = 2 '。我猜想类似于[漂亮的打印,如何拆分..](http://*.com/questions/5471556/pretty-print-windows-path-variable-how-to-split-on-in-cmd-外壳/ 5472168#5472168)可以工作。使用解析器分隔未引用的引用文本 – jeb
同样,我无法解释的异常行为,但有一个很简单的解决方案
test.bat的脚本:
echo arg1: %~1
echo arg2: %~2
echo arg3: %~3
调用:
test.bat "a=b" c
输出:
arg1: a=b
arg2: c
arg3:
注意只要改变你的论点引用,那么这样调用时,引用参数使用波形符号(〜)引用参数会从参数中删除前导/尾随引号。
确实,这不是解决方案。从OP'我不希望脚本的用户解释这个怪癖并引用“a = b”,这是违反直觉的。“ – jeb
我错过了那部分,但不是所有的直觉都是平等的。对我而言,阅读说明是“直观的”(RTFM,即cmd.exe /?)。它解释了cmd.exe如何解析命令行引号,但没有“=”符号解释。在解决Perl中创建的命令字符串问题时,此问题帮助我识别此问题。复杂的,非直观的解析器代码是一个批处理文件解决方案,但它并没有教导cmd.exe底层设计的基本现实。一旦你明白了,简单适当的引用解决方案适用于所有调用cmd.exe的脚本shell,包括Batch,Perl,Python等。 – tahoar
逗号,分号和等号是命令参数和FOR集的分隔符。通过分隔符的唯一方法是用引号括起来:test“a = b”c – Aacini
使用'enabledelayedexpansion'并假定第一个参数仍在%1中,相当干净的解决方案可能如下所示: 'setlocal enabledelayedexpansion& set args =%*& if'%1“==”%KNOA_PROFILE%“(\t set args =!args:%1 =!)'_split lines on'&',我无法输入多行comments_ – eremmel