The problematic part is call set "$$%%random%%=%%a"
. To explain why, let us start from the beginning.
Simply using set "$$%random%=%%a"
would not work, because %random%
would be expanded (read) when the entire for /F
loop, or more generally spoken, the whole block of code, is parsed, so the same value would be returned for every loop iteration. The call
trick, toghether with doubled percent signs around variables, introduces another parsing phase, so when the entire for /F
loop is parsed, call set "$$%%random%%=%%a"
is interpreted as call set "$$%random%=%a"
, because each %%
in a batch file is interpreted as one literal %
. Hence the random
variable is not yet expanded.
For every loop iteration, the for /F
variable becomes replaced by the current iteration value. call
initiates another parsing phase as already mentioned, so %random%
becomes replaced by a individual random number per loop iteration. Assuming that %a
is abritrary string
, the line is interpreted as call set "$$%random%=arbitrary string"
first, and after the second parsing phase it becomes set "$$16384=arbitrary string"
, supposing %random%
returns the number 16384
.
Now let us take another example value for %a
which contains a percent sign, like string with % sign
: after the first parsing phase, we have call set "$$%random%=string with % sign"
; after the second one, we get something like set "$$32767=string with sign"
. The percent sign disappears here, because the command line interpreter encounters a single %
sign that cannot be paired with another one (where a variable name is expected in between them, like %random%
), so it simply dismisses that character. In case %a
contained two %
signs, for instance, more % signs % here
, the result would be more here
(most likely), because SPACEsigns
SPACE, being in between a pair of %
signs, would be treated as the name of a (undefined, hence empty) variable.
To solve that issue, we need to avoid %a
to be passed through a second parsing phase. This can be achieved by avoiding %%a
to appear in the call
command line.
So to overcome the loss of %
signs in your script, you need to store %%a
into an interim variable and to do the same double-%
expansion within the call
command line like you already do for random
:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set /A "index=0"
for /f "usebackq delims=" %%a in ("G:\Playlists\Onesies.m3u8") do (
set "item=%%a"
call set "$$%%random%%.%%index%%=%%item%%"
set /A "index+=1"
)
> "G:\Playlists\Onesies.m3u8" (
for /f "tokens=1,* delims==" %%a in ('set $$') do echo(%%b
)
echo Playlist "Onesies.m3u8" has been created in the folder "G:\Playlists\".
echo/
endlocal
pause
An alternative method is to apply delayed expansion:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Toggling delayed expansion in the loop is necessary to not lose exclamation marks.
set /A "index=0"
for /f "usebackq delims=" %%a in ("G:\Playlists\Onesies.m3u8") do (
setlocal EnableDelayedExpansion
rem // This loop passes `random` over the `endlocal` barrier:
for /f %%b in ("!random!.!index!") do (
endlocal
set "$$%%b=%%a"
)
set /A "index+=1"
)
> "G:\Playlists\Onesies.m3u8" (
for /f "tokens=1,* delims==" %%a in ('set $$') do echo(%%b
)
echo Playlist "Onesies.m3u8" has been created in the folder "G:\Playlists\".
echo/
endlocal
pause
The original code from the question relies on the built-in random number variable random
which may return duplicate values. This leads to the problem that some lines of the read file get lost.
To resolve this issue, both of the above approaches herein feature a counter called index
which is of course unique for every single line that is read by the for /F
loop. This counter value is appended to the random value so that the total concatenated string is unique. Therefore, none of the lines of the read file are lost any more, opposed to the original script.