有问题的部分是call set "$$%%random%%=%%a"。为了解释原因,让我们从头开始。
简单地使用set "$$%random%=%%a" 是行不通的,因为%random% 会在整个for /F 循环或更一般地说,整个代码块被解析时被扩展(读取),所以相同的值将是为每个循环迭代返回。 call 技巧与变量周围的双百分号一起引入了另一个解析阶段,因此当解析整个 for /F 循环时,call set "$$%%random%%=%%a" 被解释为 call set "$$%random%=%a",因为批处理文件中的每个 %% 都是解释为一个文字%。因此,random 变量尚未展开。
对于每次循环迭代,for /F 变量将替换为当前迭代值。 call 启动另一个解析阶段,如前所述,因此 %random% 在每次循环迭代时被一个单独的随机数替换。假设%a是abritrary string,则该行首先被解释为call set "$$%random%=arbitrary string",在第二个解析阶段之后它变成set "$$16384=arbitrary string",假设%random%返回数字16384。
现在让我们以包含百分号的%a 的另一个示例值为例,例如string with % sign:在第一个解析阶段之后,我们有call set "$$%random%=string with % sign";在第二个之后,我们得到类似set "$$32767=string with sign" 的信息。百分号在这里消失了,因为命令行解释器遇到一个不能与另一个配对的% 符号(它们之间应该有一个变量名,比如%random%),所以它只是忽略了那个字符。如果%a 包含两个% 符号,例如more % signs % here,则结果将是more here(很可能),因为SPACEsignsSPACE 位于一对% 符号之间,将被视为(未定义,因此为空)变量的名称。
为了解决这个问题,我们需要避免 %a 通过第二个解析阶段。这可以通过避免%%a 出现在call 命令行中来实现。
因此,为了克服脚本中% 符号的丢失,您需要将%%a 存储到一个临时变量中,并在call 命令行中执行相同的双% 扩展,就像您已经做的那样对于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
另一种方法是申请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
问题中的原始代码依赖于内置随机数变量random,它可能会返回重复值。这会导致读取文件的某些行丢失的问题。
为了解决这个问题,上述两种方法都具有一个称为index 的计数器,对于for /F 循环读取的每一行来说,它当然是唯一的。此计数器值附加到随机值,以便连接的总字符串是唯一的。因此,与原始脚本相反,读取文件的任何行都不会丢失。