【问题标题】:How to receive even the strangest command line parameters?如何接收最奇怪的命令行参数?
【发布时间】:2011-05-11 03:53:39
【问题描述】:

正如在另一个线程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 循环,但我不知道如何更改它。
也许还有另一个简单的解决方案。

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

【问题讨论】:

    标签: batch-file escaping


    【解决方案1】:

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

    @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】:

      下面的代码是基于DosTipsthis answerjeb上漫无边际的Foolproof Counting of Arguments主题:

      @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
      

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

      • 未找到参数
      • 多行参数
      • 空参数(最后一个参数允许""""
      • 参数值中的一个或多个引号 (")

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

      【讨论】:

        【解决方案3】:

        我发明了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】:

          由键入命令的用户来转义任何特殊字符。在程序运行之前,您的程序无法对 shell 所做的事情做任何事情。对此没有其他“防弹”解决方案。

          【讨论】:

          • 这一点很清楚,很明显abc"&"^&def会被翻译成abc"&"&def,但是还是不能用简单的set "var=%~ 1"
          • 然后,用户需要预测该参数在批处理文件中使用了多少次,以便为其添加适当的转义级别。我猜这不太好用;-)
          猜你喜欢
          • 2022-01-10
          • 2017-10-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-03-28
          • 1970-01-01
          相关资源
          最近更新 更多