代码之家  ›  专栏  ›  技术社区  ›  jeb

如何接收最奇怪的命令行参数?

  •  14
  • jeb  · 技术社区  · 14 年前

    如另一篇文章所述 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个 不是那个 %一个

    在这种情况下 回音%1 会有用的。
    这显然是for循环,但我不知道如何改变。
    也许还有另一个简单的解决办法。

    我不需要这个来解决实际的问题,但是我喜欢在每种情况下都是防弹的解决方案,不仅仅是在大多数情况下。

    4 回复  |  直到 7 年前
        1
  •  9
  •   dbenham    12 年前

    我认为没有人在这方面发现任何漏洞,除了无法读取参数中的换行:

    @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 一块。

        2
  •  1
  •   Sponge Belly    6 年前

    下面的代码是基于 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
    

    下列情况将立即终止程序:

    • 找不到参数
    • 多行参数
    • 空参数( """ ,或 " 允许用于最后一个参数)
    • 一个或多个引号( " )在参数值中

    为了缓解这些限制,只需注释掉相关行即可。阅读内联注释了解更多信息。不要试图关闭多行参数陷阱!

        3
  •  1
  •   jeb    5 年前

    我发明了 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 
    
        4
  •  0
  •   EMP    14 年前

    这取决于键入命令以转义任何特殊字符的用户。在程序运行之前,您的程序不能做任何关于shell所做的事情。没有其他的“防弹”解决方案。