在DisableDelayedExpansion ENDLOCAL

时间:2016-09-23 13:42:35

标签: batch-file

@echo off
setlocal EnableDelayedExpansion
set /a N=0
for /f "tokens=* delims=" %%g in ('dir !FOLDERPATH! /b') do (
setlocal DisableDelayedExpansion
  set "item=%%g"
endlocal
  set /a N+=1
REM next line loses exclamation marks. replacing %%g with %%item%% gives error: not defined $$ variable
  call set "$$%%N%%=%%g"
  )
set "ind=%N%"
REM View the results
echo !ind!
for /f "tokens=* delims=" %%i in ('set $$') do echo %%i
pause
EXIT /b

我为我的问题阅读了很多解决方案(stackoverflow.com/questions/3262287; stackoverflow.com/questions/28682268; stackoverflow.com/questions/29869394; stackoverflow.com/questions/3262287),我仍然感到困惑。任何帮助表示赞赏

我有一个包含感叹号(以及百分号和&符号)的mp3文件名的文件夹。上面的子例程应该用这些文件名填充array = $$。它被调用2000次,每次都用于不同的文件夹。

我想使用EnableDelayedExpansion作为主setlocal(快两倍)。在我的整个批处理程序中,这只是一行(设置" item = %% g"),我需要DisableDelayedExpansion。我需要知道如何有效地将此变量(项)传递到DisableDelayedExpansion endlocal边界(并进入$$集)。或者我想我可以填充禁用环境中的$$集,然后我需要跨越边界传递集合。

我正在寻找速度。我可以在整个子程序中使用Disabled,但这会使我的处理时间加倍(对于非常少的感叹号和百分号)。

根据回复,我认为包含整个批处理脚本(简化)可能会很好。这个脚本需要28分钟才能在我的小机器上运行我的数据库。它处理感叹号,百分号,&符号和其他任何你可以扔的东西。

@echo off
chcp 1254>nul
setlocal DisableDelayedExpansion
set /p COUNT=Select Desired Number of Random Episodes per Album:
for /d %%f in (H:\itunes\Podcasts\*) do (
  set buffer="%%f"
  set /a ind = 0
  call:Set$$Variables
setlocal EnableDelayedExpansion
  if !COUNT! LEQ !ind! ( set DCOUNT=!COUNT! ) ELSE set DCOUNT=!ind!
  for /l %%g in (1, 1, !DCOUNT!) do (
    call:GenerateUniqueRandomNumber
    for %%N in (!num!) do echo !$$%%N!>>"%USERPROFILE%\Desktop\RandomEpisodes.m3u8"
    )
endlocal
  )
pause
Exit /b

:Set$$Variables
set /a N = 0
for /f "tokens=* delims=" %%g in ('dir %buffer% /b') do (
  set "item=%%g"
  set /a N+=1
  call set "$$%%N%%=%%item%%"
  )
set "ind=%N%"
EXIT /b

:GenerateUniqueRandomNumber
:nextone
set /a "num = (((!random! & 1) * 1073741824) + (!random! * 32768) + !random!) %% !ind! + 1"
for %%N in (!num!) do (
  if !RN%%N!==1 (
  goto:nextone
  )
  set "RN%%N=1"
)
EXIT /b

对以下内容进行简单更改,并在13分钟内完成。但它没有处理惊叹号(bangs =!)。

@echo off
chcp 1254>nul
setlocal EnableDelayedExpansion
set /p COUNT=Select Desired Number of Random Episodes per Album:
for /d %%f in (H:\itunes\Podcasts\*) do (\
setlocal
  set buffer="%%f"
  set /a ind = 0
  call:Set$$Variables
  if !COUNT! LEQ !ind! ( set DCOUNT=!COUNT! ) ELSE set DCOUNT=!ind!
  for /l %%g in (1, 1, !DCOUNT!) do (
    call:GenerateUniqueRandomNumber
    for %%N in (!num!) do echo !$$%%N!>>"%USERPROFILE%\Desktop\RandomEpisodes.m3u8"
    )
endlocal
  )
pause
Exit /b

:Set$$Variables
set /a N = 0
for /f "tokens=* delims=" %%g in ('dir %buffer% /b') do (
  set "item=%%g"
  set /a N+=1
  call set "$$%%N%%=%%item%%"
  )
set "ind=%N%"
EXIT /b

:GenerateUniqueRandomNumber
:nextone
set /a "num = (((!random! & 1) * 1073741824) + (!random! * 32768) + !random!) %% !ind! + 1"
for %%N in (!num!) do (
  if !RN%%N!==1 (
  goto:nextone
  )
  set "RN%%N=1"
)
EXIT /b

处理包含感叹号的极少数文件名的处理时间增加了一倍多。资源 - 时间值是子例程Set $$ Variables。 所以我希望有人可以在这两次之间找到一个快乐的中间地带:13分钟对28分钟。在我看来,在15分钟的差异中必须有办法处理十几个受影响的文件。

6 个答案:

答案 0 :(得分:3)

如果你真的明白什么会降低你的脚本速度,那就最好。

以下都会导致不可接受的表现:

  • 循环中过多的CALL
  • 随机选择一个号码,直到你找到一个尚未被选中的号码
  • 每个文件的附加模式重定向 - 最好只重定向一次

启用和禁用延迟扩展通常不会对性能不佳产生太大影响(除非您有大量的环境空间)

我的代码使用FINDSTR技术快速构建阵列,无需延迟扩展。

然后,我可以为每个文件夹启用一次延迟扩展。我永远不必在endlocal" barrier"

中传递任何值

我保证每个随机操作通过构建可能数字的列表来选择可用文件,固定宽度为4,带有前导空格。该列表必须适合单个变量,最大长度为~8190字节,因此该解决方案最多支持每个文件夹约2040个文件。每个随机数指定从列表中取出的位置,然后提取值并减少计数。

我将整个外部循环包含在一组额外的括号中,这样我只需要重定向一次。

我非常确定这段代码会比你不支持的第二段代码快得多!等

<强> benham1.bat

@echo off
chcp 1254>nul
setlocal DisableDelayedExpansion
set /p "maxCnt=Select Desired Number of Random Episodes per Album:"

pushd "H:\itunes\Podcasts"
>"%USERPROFILE%\Desktop\RandomEpisodes.m3u8" (
  for /d %%F in (*) do (
    pushd "%%F"
    set "fileCnt=0"
    for /f "delims=: tokens=1,2" %%A in ('dir /b /a-d 2^>nul^|findstr /n "^"') do (
      set "$$%%A=%%B"
      set "fileCnt=%%A"
    )
    setlocal enableDelayedExpansion
    set "nums="
    for /l %%N in (1 1 !fileCnt!) do (
      set "n=   %%N"
      set "nums=!nums!!n:~-4!"
    )
    if !fileCnt! lss !maxCnt! (set "cnt=!fileCnt!") else set "cnt=!maxCnt!"
    for /l %%N in (1 1 !cnt!) do (
      set /a "pos=(!random!%%fileCnt)*4, next=pos+4, fileCnt-=1"
      for /f "tokens=1,2" %%A in ("!pos! !next!") do (
        for /f %%N in ("!nums:~%%A,4!") do echo !$$%%N!
        set "nums=!nums:~0,%%A!!nums:~%%B!"
      )
    )
    endlocal
    popd
  )
)
popd

更新和解决方案

令我感到困扰的是,这些代码在RKO的手中表现得如此糟糕(见他的评论)。所以我做了一些我自己的测试来弄清楚发生了什么。

我在500个文件夹中运行代码,每个文件夹中有100个文件,我要求为每个文件夹输出50个文件。我修改了循环以将每个文件夹的名称打印到stderr,以便我可以监视进度。正如所料,每个文件夹在开始时都处理得非常快。但随着程序的进展,每个文件夹都以非线性的方式变得比前一个慢。到它结束时,每个文件夹都很慢。

处理500个文件夹花了大约3分钟。然后我把文件夹的数量增加了一倍,时间爆炸到了大约18分钟。非常讨厌。

回过头来的时候,DosTips的一群人试图调查cmd.exe如何管理环境空间,以及大环境如何影响性能。见Why does SET performance degrade as environment size grows?

我们确定ENDLOCAL实际上没有释放分配的内存,这会导致SET性能随着SET操作的累积次数而降低。这个问题有很多的SET操作,所以它变得很慢是有意义的。

但是RKO的代码有很多低效率,而且效果比我的好。在没有环境大小问题的情况下,他的代码应该 更慢。所以他的代码不能像我一样积累内存。所以我继续寻求隔离每个文件夹的内存分配。

我的第一次尝试是在新的cmd.exe进程中为单个文件夹分配所有内存。它奏效了!处理500个文件夹现在需要大约1分钟,加倍到1000个文件夹基本上将时间加倍到~2分钟!

<强> benham2.bat

@echo off
if "%~1" equ ":processFolder" (
  pushd "%folder%"
  set "fileCnt=0"
  for /f "delims=: tokens=1,2" %%A in ('dir /b /a-d 2^>nul^|findstr /n "^"') do (
    set "$$%%A=%%B"
    set "fileCnt=%%A"
  )
  setlocal enableDelayedExpansion
  set "nums="
  for /l %%N in (1 1 !fileCnt!) do (
    set "n=   %%N"
    set "nums=!nums!!n:~-4!"
  )
  if !fileCnt! lss !maxCnt! (set "cnt=!fileCnt!") else set "cnt=!maxCnt!"
  for /l %%N in (1 1 !cnt!) do (
    set /a "pos=(!random!%%fileCnt)*4, next=pos+4, fileCnt-=1"
    for /f "tokens=1,2" %%A in ("!pos! !next!") do (
      for /f %%N in ("!nums:~%%A,4!") do echo !$$%%N!
      set "nums=!nums:~0,%%A!!nums:~%%B!"
    )
  )
  popd
  exit
)

setlocal DisableDelayedExpansion
set /p "maxCnt=Select Desired Number of Random Episodes per Album:"

pushd "H:\itunes\Podcasts"
>"%USERPROFILE%\Desktop\RandomEpisodes.m3u8" (
  for /d %%F in (*) do (
    set "folder=%%F"
    cmd /v:off /c ^""%~f0" :processFolder^"
  )
)
popd
exit /b

然后我意识到我没有为我原来的benham1代码的每次测试重新启动我的控制台 - 当批处理脚本终止时,内存似乎已经重置,因为下一次运行会以同样快的速度启动作为前一个。

所以我想,为什么不简单地调用a:子例程而不是启动新的cmd.exe。这个工作大致相同,只是更好一点!

<强> benham3.bat

@echo off
setlocal DisableDelayedExpansion
set /p "maxCnt=Select Desired Number of Random Episodes per Album:"

pushd "H:\itunes\Podcasts"
>"%USERPROFILE%\Desktop\RandomEpisodes.m3u8" (
  for /d %%F in (*) do (
    set "folder=%%F"
    call :go
  )
)
popd
exit /b

:go
setlocal
cd %folder%
set "fileCnt=0"
for /f "delims=: tokens=1,2" %%A in ('dir /b /a-d 2^>nul^|findstr /n "^"') do (
  set "$$%%A=%%B"
  set "fileCnt=%%A"
)
setlocal enableDelayedExpansion
set "nums="
for /l %%N in (1 1 !fileCnt!) do (
  set "n=   %%N"
  set "nums=!nums!!n:~-4!"
)
if !fileCnt! lss !maxCnt! (set "cnt=!fileCnt!") else set "cnt=!maxCnt!"
for /l %%N in (1 1 !cnt!) do (
  set /a "pos=(!random!%%fileCnt)*4, next=pos+4, fileCnt-=1"
  for /f "tokens=1,2" %%A in ("!pos! !next!") do (
    for /f %%N in ("!nums:~%%A,4!") do echo !$$%%N!
    set "nums=!nums:~0,%%A!!nums:~%%B!"
  )
)
exit /b

另一次更新

我替换了Aacini优越的基于数组的方法,以保证每个随机操作选择一个唯一的文件名来代替我的基于字符串的方法。它会产生稍好的性能:

<强>贝纳姆-aacini.bat

@echo off
setlocal DisableDelayedExpansion
set /p "maxCnt=Select Desired Number of Random Episodes per Album:"

pushd "H:\itunes\Podcasts"    
>"%USERPROFILE%\Desktop\RandomEpisodes.m3u8" (
  for /d %%F in (*) do (
    set "folder=%%F"
    call :go
  )
)
popd
exit /b

:go
setlocal
cd "%folder%"
set "fileCnt=0"
for /f "delims=: tokens=1,2" %%A in ('dir /b /a-d 2^>nul^|findstr /n "^"') do (
  set "$$%%A=%%B"
  set /a "fileCnt=%%A, RN%%A=%%A"
)
setlocal enableDelayedExpansion
if !fileCnt! lss !maxCnt! (set end=1) else set /a "end=fileCnt-maxCnt+1"
for /l %%N in (!fileCnt! -1 !end!) do (
  set /a "ran=!random!%%%%N+1"
  set /a "num=RN!ran!, RN!ran!=RN%%N
  for %%N in (!num!) do echo !cd!\!$$%%N!
)
exit /b

以下是每个版本的时间安排摘要:

folders  | benham1  benham2   benham3   benham-aacini
---------+-------------------------------------------
    500  |    2:49     0:53      0:43       0:41
   1000  |   17:48     1:56      1:44       1:34

所以我们在DosTips上的原始思维认为环境从未缩小是错误的。但是我没有时间进行全面测试并确定它何时收缩。

无论如何,我认为我的版本真的比你当前的13分钟代码更快: - )

又一次更新(假设几次碰撞)

整套文件中的多个随机选择可能会导致重复。我的代码假定请求的文件数量可能是大型列表的很大一部分,在这种情况下,您可能会获得许多重复项。

所以我之前的所有解决方案都做了一些额外的记账,以保证每个随机操作都会产生一个独特的文件。

但RKO似乎通常只从每个文件夹中选择几个文件。所以碰撞的机会很小。他的代码只是随机选择一个文件,然后如果之前选择了该文件,它会循环返回并再次尝试,直到找到一个新文件。但由于碰撞的机会很小,重试很少发生。这种方法的簿记量明显减少。结果是,只要请求的数量仍然很小,他的随机选择算法就会更快。

所以我采用了我的代码来使用稍微修改过的RKO选择方法。我希望这对于小额请求来说是最快的。

<强> benham4.bat

@echo off
setlocal DisableDelayedExpansion
set /p "maxCnt=Select Desired Number of Random Episodes per Album:"

pushd "H:\itunes\Podcasts"    
>"%USERPROFILE%\Desktop\RandomEpisodes.m3u8" (
  for /d %%F in (*) do (
    set "folder=%%F"
    call :selectRandom
  )
)
popd
exit /b

:selectRandom
setlocal
cd "%folder%"
set "fileCnt=0"
for /f "delims=: tokens=1,2" %%A in ('dir /b /a-d 2^>nul^|findstr /n "^"') do (
  set "$$%%A=%%B"
  set "fileCnt=%%A"
)
setlocal enableDelayedExpansion
if !fileCnt! lss !maxCnt! (set /a end=fileCnt) else set /a end=maxCnt
for /l %%N in (1 1 !end!) do (
  set /a "N=!random!%%fileCnt+1"
  if not defined $$!N! call :tryAgain
  for %%N in (!N!) do echo !folder!\!$$%%N!
  set "$$!N!="
)
exit /b

:tryAgain
set /a "N=!random!%%fileCnt+1"
if not defined $$!N! goto :tryAgain
exit /b

答案 1 :(得分:1)

没有任何好的方法可以将许多变量转移到范围之外(通过endlocal屏障)。

但是当你一个接一个地转移它而不是它的作用。

@echo off
setlocal
set "folderPath=C:\temp"
call :func
set $$
exit /b

:func
setlocal EnableDelayedExpansion
FOR /F "delims=" %%F in ("!FOLDERPATH!") DO (
    endlocal
    setlocal DisableDelayedExpansion
    set /a Counter=0
    for /f "tokens=* delims=" %%g in ('dir %%F /b') do (        
        set "item=%%g"
        setlocal EnableDelayedExpansion
        FOR /F "tokens=1,*" %%C in ("!Counter! !item!") DO (

            endlocal
            endlocal
            set "$$%%C=%%D"
            setlocal DisableDelayedExpansion
            set /a Counter=%%C + 1
        )
    )
)

EXIT /b

此解决方案假设您的文件名中没有任何文件名包含爆炸!或您从禁用的延迟扩展上下文中调用您的函数。

另一种解决方案
如果您需要防弹变体,则可以将endlocal/set "$$%%C=%%D"替换为macroReturn technic

当您可以使用临时文件时,您也可以使用set /p技术。

:collectFiles
dir /b !FOLDERPATH! > temp.$$$
FOR /F %%C in ('type temp.$$$ ^| find /v /c ""') DO (
    echo count %%C
    < temp.$$$ (
        for /L %%n in (0 1 %%C) DO (
            set /p $$%%n=
        )
    )
)
exit /b

答案 2 :(得分:1)

此方法以非常快的方式在禁用的延迟扩展环境中创建阵列:

@echo off
setlocal DisableDelayedExpansion

for /f "tokens=1* delims=:" %%g in ('dir %FOLDERPATH% /b ^| findstr /N "^"') do (
   set "$$%%g=%%h"
   set "ind=%%g"
)

REM View the results
echo %ind%
set $$

REM View the results using DelayedExpansion
setlocal EnableDelayedExpansion
for /L %%i in (1,1,%ind%) do echo %%i- !$$%%i!

pause
EXIT /b

您也可以通过一种非常简单的方式将整个数组传输到调用者程序的环境中:

for /F "delims=" %%a in ('set $$') do (
   endlocal
   set "%%a"
)

在这种方法中,endlocal命令执行了几次,但它只是在与函数的初始setlocal匹配时才第一次运行。如果此方法可以释放其他先前的环境,那么只需添加一个简单的测试:

set _FLAG_=1
for /F "delims=" %%a in ('set $$') do (
   if defined _FLAG_ endlocal
   set "%%a"
)

编辑添加了完整的示例代码

我在OP发布完整的示例代码后编写了下面的代码;我使用了原始代码中的相同命令和变量名称。此解决方案使用我最初在此处发布的方法在禁用的延迟扩展环境中创建数组(通过findstr /N "^"命令生成元素的索引),然后使用非常随机的顺序提取数组的元素我已在this answer使用的有效方法。我还插入了一些提高效率的修改,例如避免call命令,并通过标准更改追加重定向>>(每次输出行执行一次)重定向>(只执行一次)。生成的程序应该比原始的OP代码运行得快得多。

@echo off
chcp 1254>nul
setlocal DisableDelayedExpansion
set /p COUNT=Select Desired Number of Random Episodes per Album:

(for /d %%f in (H:\itunes\Podcasts\*) do (
   for /f "tokens=1* delims=:" %%g in ('dir "%%f" /b /A-D 2^>NUL ^| findstr /N "^"') do (
      set "$$%%g=%%h"
      set "ind=%%g"
      set "RN%%g=%%g"
   )
   setlocal EnableDelayedExpansion
   if %COUNT% LEQ !ind! (set /A DCOUNT=ind-COUNT+1) ELSE set DCOUNT=1
   for /l %%g in (!ind!, -1, !DCOUNT!) do (
      set /A "ran=(!random!*%%g)/32768+1"
      set /A "num=RN!ran!, RN!ran!=RN%%g"
      for %%N in (!num!) do echo !$$%%N!
   )
   endlocal
)) > "%USERPROFILE%\Desktop\RandomEpisodes.m3u8"

pause
Exit /b

2ND EDIT

在我们的原始方法中阅读了用户dbenham关于环境发布问题的解释之后,我在我的代码中引入了他建议的相同修改以解决问题。预计现在这两个代码的运行速度都比最初的OP代码快......

@echo off
chcp 1254>nul
setlocal DisableDelayedExpansion
(
for /F "delims==" %%a in ('set') do set "%%a="
set "ComSpec=%ComSpec%"
set "USERPROFILE=%USERPROFILE%"
)
set /p COUNT=Select Desired Number of Random Episodes per Album:
(for /d %%f in (H:\itunes\Podcasts\*) do call :Sub "%%f"
) > "%USERPROFILE%\Desktop\RandomEpisodes.m3u8"
pause
Exit /b

:Sub
setlocal DisableDelayedExpansion
   cd %1
   for /f "tokens=1* delims=:" %%g in ('dir /b /A-D 2^>NUL ^| findstr /N "^"') do (
      set "$%%g=%%h"
      set /A "ind=%%g, N%%g=%%g"
   )
   setlocal EnableDelayedExpansion
   if %COUNT% LEQ %ind% (set /A DCOUNT=ind-COUNT+1) ELSE set DCOUNT=1
   for /l %%g in (%ind%, -1, %DCOUNT%) do (
      set /A "ran=!random!%%%%g+1"
      set /A "num=N!ran!, N!ran!=N%%g"
      for %%N in (!num!) do echo %CD%\!$%%N!
   )
exit /B

3RD EDIT

生成dbenham和我使用的唯一随机数的方法是一种有效管理大多数情况的通用方法;然而,似乎这种方法不适合这个特定问题。下面的新代码是尝试为这个以最快的方式运行的问题编写解决方案。

Mod:修复了一个小错误。

@echo off
chcp 1254>nul
setlocal DisableDelayedExpansion
set "findstr=C:\Windows\System32\findstr.exe"
for /F "delims=" %%a in ('where findstr 2^>NUL') do set "findstr=%%a"
(
for /F "delims==" %%a in ('set') do set "%%a="
set "ComSpec=%ComSpec%"
set "USERPROFILE=%USERPROFILE%"
set "findstr=%findstr%"
)
set /p COUNT=Select Desired Number of Random Episodes per Album:
(for /d %%f in (H:\itunes\Podcasts\*) do call :Sub "%%f"
) > "%USERPROFILE%\Desktop\RandomEpisodes.m3u8"
pause
Exit /b

:Sub
setlocal DisableDelayedExpansion
   cd %1
   for /f "tokens=1* delims=:" %%g in ('dir /b /A-D 2^>NUL ^| "%findstr%" /N "^"') do (
      set "$%%g=%%h"
      set "ind=%%g"
   )
   setlocal EnableDelayedExpansion
   if %COUNT% LEQ %ind% (set "DCOUNT=%COUNT%") ELSE set "DCOUNT=%ind%"
   for /l %%g in (1, 1, %DCOUNT%) do (
      set /A "num=!random!%%%%g+1"
      if not defined $!num! call :nextNum
      for %%N in (!num!) do echo %CD%\!$%%N!& set "$%%N="
   )
exit /B

:nextNum
   set /A num=num%%ind+1
   if not defined $%num% goto nextNum
exit /B

答案 3 :(得分:1)

10分钟!它是原始帖子中显示的13 Minute EnabledDelayedExpansion版本的修改版本。我正在等待来自dbenham的轻微编码修复,看看他是否是一个更快的解决方案。

这是关键的编码:

for /f "tokens=* delims=" %%g in ('dir "!buffer!" /b') do (
  setlocal DisableDelayedExpansion
  for /f "tokens=1* delims=!" %%m in ("%%g") do if not "%%m"=="%%g" (
    set "item=%%g"
    call set BangFile=%%item:^&=¬%%
    call set BangFile=%%Bangfile:!=^^^^!%%
    call echo %%BangFile%%>"G:\BangFilename.txt"
)
  endlocal

是的,这很难看。我不喜欢写临时文件,但没有别的办法。而且在整个120K集合中可能只有几十个包含bangs(!)的文件名,所以很少有读写。我不得不两次使用上面的代码:一次是目录名,一次是文件名。

完整的代码在这里:

@echo off
chcp 1254>nul
setlocal EnableDelayedExpansion
IF EXIST "%USERPROFILE%\Desktop\RandomEpisodes.m3u8" del "%USERPROFILE%\Desktop\RandomEpisodes.m3u8"
set /p COUNT=Select Desired Number of Random Episodes per Album:
call:timestart
for /d %%f in (G:\itunes\Podcasts\* H:\itunes\Podcasts\*) do (

setlocal DisableDelayedExpansion
  if exist g:\BangDirectory.txt del g:\BangDirectory.txt
  for /f "tokens=1* delims=!" %%d in ("%%f") do if not "%%d"=="%%f" (
    set "BangDir=%%f"
    call set BangDir=%%BangDir:^&=¬%%
    call set BangDir=%%BangDir:!=^^^^!%%
    call echo %%BangDir%%>g:\BangDirectory.txt
  )
endlocal

setlocal
  set "buffer=%%f"
  set directory=%%~nf
  call:timecalc

  call:Set$$Variables

  if !COUNT! LEQ !ind! ( set DCOUNT=!COUNT! ) ELSE set DCOUNT=!ind!
  for /l %%g in (1, 1, !DCOUNT!) do (
    call:GenerateUniqueRandomNumber

    for %%N in (!num!) do echo !$$%%N!>>"%USERPROFILE%\Desktop\RandomEpisodes.m3u8"
    )
endlocal
  )
pause
Exit /b

:Set$$Variables
set /a cnt = 0
  if exist g:\BangDirectory.txt for /f "usebackq delims=" %%t in ("G:\BangDirectory.txt") do (
  set "buffer=%%t"
  set "buffer=!buffer:¬=&!"
  )
for /f "tokens=* delims=" %%g in ('dir "!buffer!" /b') do (
  setlocal DisableDelayedExpansion
  if exist g:\BangFilename.txt del g:\BangFilename.txt
  for /f "tokens=1* delims=!" %%m in ("%%g") do if not "%%m"=="%%g" (
    set "item=%%g"
    call set BangFile=%%item:^&=¬%%
    call set BangFile=%%Bangfile:!=^^^^!%%
    call echo %%BangFile%%>"G:\BangFilename.txt"
)
  endlocal

  if exist g:\BangFilename.txt for /f "usebackq tokens=* delims=" %%p in ("G:\BangFilename.txt") do (
  set Filename=%%p
  set "Filename=!Filename:¬=&!"
  set $$!cnt!=!buffer!\!Filename!
  )
  if not exist g:\BangFilename.txt set "$$!cnt!=!buffer!\%%g"
  set /a cnt+=1
)
set "ind=!cnt!"
EXIT /b

:GenerateUniqueRandomNumber
:nextone
set /a "num = (((!random! & 1) * 1073741824) + (!random! * 32768) + !random!) %% !ind!"
for %%N in (!num!) do (
  if !RN%%N!==1 (
  goto:nextone
  )
  set "RN%%N=1"
)
EXIT /b

:timestart
for /F "tokens=1-4 delims=:.," %%a in ("%time%") do (
   set /A "start=(((%%a*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100")
exit /b

:timecalc
REM Get end time:
for /F "tokens=1-4 delims=:.," %%a in ("%time%") do (
   set /A "end=(((%%a*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100"
)
REM Get elapsed time:
set /A elapsed=end-start
REM Show elapsed time:
set /A hh=elapsed/(60*60*100), rest=elapsed%%(60*60*100), mm=rest/(60*100), rest%%=60*100, ss=rest/100, cc=rest%%100
if %mm% lss 10 set mm=0%mm%
if %ss% lss 10 set ss=0%ss%
set "TimeElapsed=    Time elapsed (mm:ss) %mm%:%ss%"
title %timeelapsed%    WIP: "%directory%"
exit /b

关于测试的注意事项。我测试了所有版本的病毒防护关闭,没有其他正在运行的程序。我选择了5作为所需的随机数集。我使用运行Win XP的Evo N410c。我尝试尽可能地为每个版本的脚本尽可能地,但我注意到13分钟的运行有时可以快到10分钟(我的猜测是XP正在创建和保持一些影响运行时的指数)。

120K项目库包含在通过USB连接的外部硬盘驱动器上。该库有大约300个目录,包含数百到数千个项目。它有另外1400个目录,介于几个和几十个项目之间。这是一个实时库,但我包含了一个额外的测试目录,文件名包含!,&amp;的每个组合和排列。和%。

哲学笔记。有几次我不得不对这个120K项目库做'东西',最后一直是这样的情况,避免使用DisableDelayedExpansion是最好的规则。我通常会使用EnabledDelayedExpansion尽我所能,然后将任何异常(bang)作为例外处理。

非常欢迎任何减少此解决方案的丑陋(但不是速度)的建议。

后记--------------------

我将Aacini和dbenham的随机数例程纳入了我的10分钟版本。他们的编码看起来比我原来的编码更优雅。我删除了我的GenerateUniqueRandomNumber子例程并合并了以下内容:

   if !ind! lss !Count! (set end=1) else set /a "end=ind-Count+1"
   for /l %%N in (!ind! -1 !end!) do (
     set /a "ran=!random!%%%%N+1"
     set /a "num=RN!ran!, RN!ran!=RN%%N
     for %%N in (!num!) do echo !$$%%N!>>"%USERPROFILE%\Desktop\RandomEpisodes.m3u8"
   )

此更改为处理时间增加了2分钟(运行时间从10分钟增加到12分钟)。有时优雅并不像普通丑陋那么快。我坚持原着。

答案 4 :(得分:0)

...
setlocal DisableDelayedExpansion
endlocal&set "item=%%g"
...

应该有用。

答案 5 :(得分:0)

@ECHO OFF
SETLOCAL DISABLEDELAYEDEXPANSION
FOR /f "tokens=1*delims=:" %%a IN (
 'dir /b /ad c:\106x^|findstr /n /r "."') DO (
 SET "thing%%a=%%b"
)
SET thing
GOTO :EOF

这应该建立你的阵列。 c:\106x只是一个目录,我有一些奇怪的目录名。

我的目录c:\ 106x:<​​/ p>

!dir!
%a
%silly%
-
1 & 2
a silly dirname with & and % and ! an things
exe
flags
nasm32working
nasmxpts
no test file(s)
some test file(s)
stl
wadug
with spaces
with'apostrophes'test

运行代码

的结果
thing1=!dir!
thing10=nasmxpts
thing11=no test file(s)
thing12=some test file(s)
thing13=stl
thing14=wadug
thing15=with spaces
thing16=with'apostrophes'test
thing2=%a
thing3=%silly%
thing4=-
thing5=1 & 2
thing6=a silly dirname with & and % and ! an things
thing7=exe
thing8=flags
thing9=nasm32working

适合我! - 并且只执行一次setlocal