我需要为初学者解释这个批处理文件

时间:2015-08-10 05:41:38

标签: windows batch-file

@echo off
setlocal
set "myString=abcdef!%%^^()^!"
call :strlen result myString
echo %result%
goto :eof

:strlen <resultVar> <stringVar>
(   
setlocal EnableDelayedExpansion
set "s=!%~2!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" ( 
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
)
( 
endlocal
set "%~1=%len%"
exit /b
)

我从其他人那里得到了这个代码但却无法理解它是如何工作的。这不会是一个问题,除了我试图使这个批处理文件适用于从另一个批处理调用,但最终会遇到错误。如果我能理解这对于获取字符串长度的确切程度,那么我将能够定制它以便我可以使用它。

你不一定要告诉我每个命令的作用,(我确实有一些经验),但是如果你能告诉我哪些命令按照什么顺序使用我可以查找那些我还不知道的命令

%%P? 

!%~2!#? 

对我来说就像垃圾一样。

2 个答案:

答案 0 :(得分:2)

如果你坐了好几个小时并阅读了set /?的输出,那么很多事情就会变得很明显,但假设你没有时间(并且让我们面对它) ,谁在地球真的想要阅读文档?),我会尝试解释。

具体来说,帮助文本中有一点表示%x:S,L%(或等效的延迟扩展,!x:S,L!)表示x变量的子字符串,从S(从零开始)和L个字符长。

该函数的伪代码基本上是:

def strlen (byref RESULT, byval STRING):
    set str to STRING, appending "#" as well
    set len = 0
    for pos = 4096 to 1 inclusive, halving each time:
        while str has a character at position pos:
            add pos to len
            remove pos characters from start of str
        end
    end
    RESULT = len

初始字符串添加了一个字符,以便不受子字符串的不利影响,其第一个索引为零而不是一个。然后,字符串的长度将是该最终字符的位置。

其余的相对简单。 len变量初始化为零,然后在4096开始pos,如果字符串在该位置(!s:~%%P,1!" NEQ "")有一个字符,则将len增加那么多并删除从一开始就有很多人物。

一旦调整后的字符串的长度低于4096,您就开始处理2048,依此类推,直到字符串中只剩下#为止。然后找到长度并返回它(通过实际将传入的第一个变量设置为该值)。

因此,对于字符串ABCDEFGHIJK(变为ABCDEFGHIJK#),在len达到8之前,不会向pos添加任何内容。那是因为超过11的所有子串都是空的。

一旦pos达到8,那么该位置的单字符子字符串为I,因此你在长度上加8(得到8)并删除最初的8个字符,给你{{ 1}}。现在该字符串是短的,在第8位有另一个字符,因此IJK#变为4。

然后在该偏移处找不到任何字符(四个字符在偏移零,一,二和三),所以你立即转到两个。 在该位置是一个字符pos,因此长度有两个添加(变为十),字符串变为K,因为现在没有字符在偏移量为2时,K#变为一个。

如果pos位于一个位置,则该位置会有一个字符pos,因此长度会增加一个(变为十一个),字符串变为#。现在第一个位置没有字符,所以循环退出,最后的长度返回给调用者。

如果您在此之后遇到困难,请使用以下代码更好地解释它(添加了调试语句):

#

您从中看到的输出反映了我上面的解释:

@echo off
setlocal
set myString=ABCDEFGHIJK
call :strlen result myString
echo %result%
goto :eof

:strlen
    setlocal enabledelayedexpansion
    set "s=!%~2!#"
    echo.String is [!s!]
    set len=0
    for %%p in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
        echo.   Position is [%%p], string is [!s!], character is [!s:~%%p,1!]
        if "!s:~%%p,1!" NEQ "" ( 
            set /a "len+=%%p"
            set s=!s:~%%p!
            echo.      Found char, len is [!len!], string is now [!s!]
        )
    )
    endlocal && set %~1=%len%

答案 1 :(得分:1)

paxdiablo did a great job explaining高度优化的批处理脚本如何设法计算字符串的长度。

原始算法是在http://www.dostips.com/forum/viewtopic.php?p=5385开发的,您可以在其中了解它的开发方式。基本算法有很多变化。

理解它是如何工作的,这是一个值得称赞的目标,但是如果您想要做的只是开发一个可供其他脚本用来计算字符串长度的脚本,则无需理解该算法。但是,您需要了解如何开发和调用批处理&#34;功能&#34;的基础知识。 http://www.dostips.com/forum/viewtopic.php?p=5385有一个很棒的教程。

我会颠倒参数的顺序,并使return变量可选。如果未指定,则该函数可以将值打印到屏幕(标准输出)。这使得从命令行使用非常方便。

您可以将以下脚本放在名为STRLEN.BAT的文件中,并将该文件放在PATH环境变量中包含的文件夹中。我使用c:\utils作为各种批处理实用程序的存储库。

<强> STRLEN.BAT

:strlen  StrVar  [RtnVar]
::
:: Compute the length of the string in variable StrVar
:: and return the result in variable RtnVar.
:: If RtnVar is not specified, then print the result to stdout.
::
:: This code is a derivative of code developed at
:: http://www.dostips.com/forum/viewtopic.php?p=5385
::
@echo off 
setlocal EnableDelayedExpansion
set "s=!%~1!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
  if "!s:~%%P!" NEQ "" ( 
    set /a "len+=%%P"
    set "s=!s:~%%P!"
  )
)
endlocal & if "%~2" equ "" (echo %len%) else set "%~2=%len%"
exit /b

用法很简单。

从命令行打印出PATH变量的长度:

D:\test>strlen path
1330

如果要在批处理脚本中使用该实用程序,那么当然必须使用CALL。

@echo off
:: Print the length of PATH to the screen
call strlen path

echo(

:: Store the length of PATH in variable LEN, then show the result
call strlen path len
echo length of PATH = %len%

可以开发一个实用程序脚本库,可供其他脚本使用,供您自己使用。但是,如果您要分发脚本以供其他人使用,通常的做法是将例程嵌入到单个脚本中,以使分发更简单,更可靠。