正如另一个帖子How to avoid cmd.exe interpreting shell special characters like < > ^中所讨论的那样 从命令行获取所有参数并不容易。
一个简单的
set var=%1
set "var=%~1"
如果您有之类的请求,是不够的
myBatch.bat abc"&"^&def
我有一个解决方案,但它需要一个临时文件,而且它也不是防弹。
@echo off
setlocal DisableDelayedExpansion
set "prompt=X"
(
@echo on
for %%a in (4) do (
rem #%1#
)
) > XY.txt
@echo off
for /F "delims=" %%a in (xy.txt) DO (
set "param=%%a"
)
setlocal EnableDelayedExpansion
set param=!param:~7,-4!
echo param='!param!'
它失败了 myBatch.bat%a ,它显示 4 而不是%a
在这种情况下,一个简单的 echo%1 就可以了
这显然是for-loop但我不知道如何改变它。
也许存在另一个简单的解决方案。
我不需要这个来解决实际问题,但我喜欢在每种情况下都是防弹的解决方案,而不仅仅是在大多数情况下。
答案 0 :(得分:9)
除非无法读取参数中的换行符,否则我认为没有人发现任何漏洞:
@echo off
setlocal enableDelayedExpansion
set argCnt=1
:getArgs
>"%temp%\getArg.txt" <"%temp%\getArg.txt" (
setlocal disableExtensions
set prompt=#
echo on
for %%a in (%%a) do rem . %1.
echo off
endlocal
set /p "arg%argCnt%="
set /p "arg%argCnt%="
set "arg%argCnt%=!arg%argCnt%:~7,-2!"
if defined arg%argCnt% (
set /a argCnt+=1
shift /1
goto :getArgs
) else set /a argCnt-=1
)
del "%temp%\getArg.txt"
set arg
以上内容来自热烈的DosTips讨论 - http://www.dostips.com/forum/viewtopic.php?p=13002#p13002。 DosTips用户Liviu提出了关键SETLOCAL DisableExtensions
篇。
答案 1 :(得分:1)
以下代码基于Foolproof Counting of Arguments上的漫无目的DosTips主题和this answer上的jeb:
@echo off & setLocal enableExtensions disableDelayedExpansion
(call;) %= sets errorLevel to 0 =%
:: initialise variables
set "paramC=0" & set "pFile=%tmp%\param.tmp"
:loop - the main loop
:: inc param counter and reset var storing nth param
set /a paramC+=1 & set "pN="
:: ECHO is turned on, %1 is expanded inside REM, GOTO jumps over REM,
:: and the output is redirected to param file
for %%A in (%%A) do (
setLocal disableExtensions
set prompt=@
echo on
for %%B in (%%B) do (
@goto skip
rem # %1 #
) %= for B =%
:skip - do not re-use this label
@echo off
endLocal
) >"%pFile%" %= for A =%
:: count lines in param file
for /f %%A in ('
find /c /v "" ^<"%pFile%"
') do if %%A neq 5 (
>&2 echo(multiline parameter values not supported & goto die
) %= if =%
:: extract and trim param value
for /f "useBack skip=3 delims=" %%A in ("%pFile%") do (
if not defined pN set "pN=%%A"
) %= for /f =%
set "pN=%pN:~7,-3%"
:: die if param value is " or "", else trim leading/trailing quotes
if defined pN (
setLocal enableDelayedExpansion
(call) %= OR emulation =%
if !pN!==^" (call;)
if !pN!=="" (call;)
if errorLevel 1 (
for /f delims^=^ eol^= %%A in ("!pN!") do (
endLocal & set "pN=%%~A"
) %= for /f =%
) else (
>&2 echo(empty parameter values (""^) not supported & goto die
) %= if errorLevel =%
) else (
:: no more params on cmd line
set /a paramC-=1 & goto last
) %= if defined =%
:: die if param value contains "
if not "%pN:"=""%"=="%pN:"=%" (
>&2 echo(quotes (^"^) in parameter values not supported & goto die
) %= if =%
:: assign nth param, shift params, and return to start of loop
set "param%paramC%=%pN%" & shift /1 & goto loop
:last - reached end of params
:: no param values on cmd line
if %paramC% equ 0 (
>&2 echo(no parameter values found & goto die
) %= if =%
:: list params
set param
goto end
:die
(call) %= sets errorLevel to 1 =%
:end
:: exit with appropriate errorLevel
endLocal & goto :EOF
以下条件将立即终止该程序:
"""
或最后一个参数允许"
)"
)
为了简化这些限制,只需注释掉相关的行。阅读内联注释以获取更多信息。不要试图关闭多线参数陷阱!
答案 2 :(得分:0)
由用户键入命令以转义任何特殊字符。在程序运行之前,程序无法对shell执行的操作执行任何操作。没有其他“防弹”解决方案。
答案 3 :(得分:0)
我发明了syntax-error-technic来解决这个问题(部分)。
使用此解决方案,甚至可以接收多行参数以及回车符。
没有已知的参数会失败!
但是是该解决方案的缺点,主进程退出,只有子进程继续。
这是捕获技巧的结果,使用无效的括号块( Prepare ) PARAMS...
会产生语法错误。
但是语法错误本身会输出完整的块,包括%*
的扩展值。
permanent redirect technic
将输出重定向到文件。
子进程可以从文件中检索完整的参数。
当批处理文件仅处理参数并且始终在之后退出时,此解决方案可能会很有用。
@echo off
REM *** Thread redirector
for /F "tokens=3 delims=:" %%F in ("%~0") do goto %%F
REM *** Clear params.tmp
break > params.tmp
start "" /b cmd /k "%~d0\:StayAlive:\..\%~pnx0 params.tmp"
(set LF=^
%=empty=%
)
REM *** Change prompt for better recognition
prompt #PROMPT#
REM *** Change streams permanently
REM *** stream1 redirects to params.tmp
REM *** stream2 redirects to nul
echo on >nul 2>nul 0>nul 3>params.tmp 4>nul 5>&3
@REM *** This is the magic part, it forces a syntax error, the error message itself shows the expanded %asterix without ANY modification
( Prepare ) PARAMS:%LF%%*%LF%
echo Works
exit /b
REM *** Second thread to fetch and show the parameters
:StayAlive
:__WaitForParams
if %~z1 EQU 0 (
goto :__WaitForParams
)
REM *** Show the result
findstr /n "^" %1