A lot of this would become obvious if you sat down for hours and read the output of set /?
but, assuming you don't have the time for that (and, let's face it, who on Earth really wants to read documentation?), I'll try and explain.
Specifically, there's a bit in that help text that states %x:S,L%
(or the equivalent with delayed expansion, !x:S,L!
) means the substring of the x
variable, starting at S
(zero-based) and L
characters long.
The pseudo-code of that function is basically:
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
The initial string has a character added so as to not be adversely affected by the substring with its first index being zero rather than one. The length of the string will then be the position of that final character.
The rest is relatively straightforward. The len
variable is initialised to zero then, starting pos
at 4096, if the string has a character at that position (!s:~%%P,1!" NEQ ""
), increase len
by that much and remove that many characters from the start.
Once the length of the adjusted string gets below 4096
, you start working on 2048
and so on until there is only the #
left in the string. The length has then been found and you return it (by actually setting the passed in first variable to that value).
So, for the string ABCDEFGHIJK
(turned into ABCDEFGHIJK#
), nothing would be added to len
until pos
reached eight. That's because the all the substrings beyond eleven would be empty.
Once pos
reached eight however, the one-character substring at that position is I
so you add eight to the length (to get eight) and removes the initial eight characters, giving you IJK#
. The string is now to short to have another character at position eight, so pos
becomes four.
Then no character is found at that offset (the four characters are at offsets zero, one, two and three) so you go immediately to two. There is a character K
at that position so the length has two added (to become ten), the string becomes K#
and, because there's now no character at offset two, pos
becomes one.
With pos
at one, there's a character #
at that position so the length has one added (to become eleven), and the string becomes #
. Now there's no character at position one so the loop exits, and the final length is returned to the caller.
If you have a hard time following that, use the following code which explains it better (with added debug statements):
@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%
The output you see from that mirrors my explanation above:
String is [ABCDEFGHIJK#]
Position is [4096], string is [ABCDEFGHIJK#], character is []
Position is [2048], string is [ABCDEFGHIJK#], character is []
Position is [1024], string is [ABCDEFGHIJK#], character is []
Position is [512], string is [ABCDEFGHIJK#], character is []
Position is [256], string is [ABCDEFGHIJK#], character is []
Position is [128], string is [ABCDEFGHIJK#], character is []
Position is [64], string is [ABCDEFGHIJK#], character is []
Position is [32], string is [ABCDEFGHIJK#], character is []
Position is [16], string is [ABCDEFGHIJK#], character is []
Position is [8], string is [ABCDEFGHIJK#], character is [I]
Found char, len is [8], string is now [IJK#]
Position is [4], string is [IJK#], character is []
Position is [2], string is [IJK#], character is [K]
Found char, len is [10], string is now [K#]
Position is [1], string is [K#], character is [#]
Found char, len is [11], string is now [#]
11