2016-09-23 25 views
2
@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),我仍然很困惑。任何幫助讚賞保留跨DisableDelayedExpansion ENDLOCAL

我有一個包含感嘆號(和百分號和&符號)的mp3文件名的文件夾。上面的子程序應該用這些文件名填充一個數組= $$。它被稱爲2000次,每次換一個不同的文件夾。

我想使用EnableDelayedExpansion作爲主setlocal(快兩倍)。在我的整個批處理程序中,這只是行(設置「item = %% g」),我需要DisableDelayedExpansion。我需要知道如何有效地通過DisableDelayedExpansion endlocal邊界(並進入$$集)傳遞這個變量(item)。另外,我想我可以填充殘疾人環境中的$$設置,然後我需要跨越邊界傳遞設置。

我在尋找速度。我可以在整個子程序中使用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分鐘差異中的十幾個受影響的文件。

+0

有沒有你的文件名cotain'!'? – jeb

+0

你的setlocal disabledDelayedExpansion/endlocal塊不會設置任何變量,因爲它在endlocal後被銷燬 – jeb

+0

我想你的代碼通常是一個函數,或者你是否將所有代碼按順序從上到下寫入? – jeb

回答

3

如果您真正理解什麼會減慢腳本,那麼最好。

下都造成不可接受的性能:在一個循環中

  • 過度調用
  • 隨機選擇號碼,直到你得到了尚未選擇
  • 重定向追加模式爲每個文件一個 - 最好只重定向一次

啓用和禁用延遲擴展通常不會對性能造成太大影響(除非你有一個massi真正的大環境空間)

我下面的代碼使用FINDSTR技術來快速構建數組,無需延遲擴展。

然後我可以爲每個文件夾啓用一次延遲擴展。我從來沒有跨越ENDLOCAL「屏障」

我保證每個隨機操作通過建立可能的數字的列表,的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 
+0

28分鐘。謝謝你。您分享的代碼中有很多很棒的學習資料。我會喜歡破譯它。但它不是更快。我的想法是使用我的13分鐘版本,並提出特殊處理任何有驚歎號...在這方面的任何提示或提示? – RKO

+0

@RKO - 查看我更新的答案。我想我解決了我的性能問題,它應該比您當前使用的解決方案快得多。 – dbenham

+0

謝謝。我會檢查一下,但明天之前無法回覆你。我設法想出了一個解決方案(使用我的EnableDelayed版本),它暫時在10分鐘內計時。需要更多的測試。 – RKO

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

應該工作。

+0

這並不能解決數值數組問題 – jeb

+0

@jeb我回答了所問的問題。看起來實際的問題是「如何從列表中建立一組值?」 – Magoo

+1

你說得對,標題說'變量',但在代碼中,他發表了2000個項目的數組。 – jeb

1

沒有任何好的方法來轉移許多變量超出範圍(通過最終局部障礙)。

但是,當你一個接一個地轉移,而不是它的工作。

@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 
+0

如果我不擔心劉海,我的原代碼效果很好(並且速度很快)。但是我願意。我不認爲我可以忍受臨時文件。 macroReturn技術可能工作,但它的長度和複雜性讓我懷疑它可以滿足我對速度的需求(我甚至沒有嘗試過)。 – RKO

+0

「over the endlocal _barrier_」**':/'** – Aacini

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:

!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

+0

謝謝,但我試圖避免過度延長DisableDelayedExpansion ...這會殺死處理速度。再次,我有工​​作代碼,但我需要加快速度......並且我注意到,如果我可以容忍一些缺失的文件名(包含撇號的文件名),我可以將處理時間減半。因此,最初的文章非常詳細地介紹了在整個子程序中不使用DisableDelayedExpansion。 – RKO

+0

當然,它的工作原理。如果您將其更改爲禁用延遲擴展,那麼我的原始代碼也是如此。但他們都很慢。這個差異是一個因子2與啓用延遲擴展(需要兩倍長) – RKO

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編輯

後讀取用戶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編輯

產生由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 
+0

比我張貼的原始代碼慢(在您將其更改爲disabledelayedexpansion之後)...並且我的原始代碼需要使用enableddelayedexpansion兩倍,並忽略撇號。所以我的想法是在殘疾人的速度和啓用之間存在一些東西......因此我想對使用disableddelayedexpansion進行仔細的評估。 – RKO

+0

不錯的主意,但是'set'%% a「'在跳轉到已啓用的延遲擴展上下文 – jeb

+0

感謝代碼時仍會銷燬'!',但我必須在1小時20分鐘後關閉它。這只是大約3/4完成。太慢了。我提到了一些可以包含1500個文件的文件夾嗎?其他人可以只包含2. – RKO

1

10分鐘!它是原始帖子中顯示的13分鐘啓用延遲擴展版本的修改版本。我正在等待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集合中,可能只有幾十個包含劉海(!)的文件名,因此只有很少的讀寫。我不得不使用上面的代碼兩次:一次用於目錄名,一次用於文件名。

完整的代碼是在這裏:

@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個目錄,介於幾個和幾十個項目之間。這是一個實時庫,但我包含一個額外的測試目錄,文件名包含!,&和%的所有組合和排列組合。

哲學筆記。有幾次,我不得不用這個120K的物品庫來做'東西',最後一直是避免DisableDelayedExpansion是最好的規則。我通常盡我所能用EnabledDelayedExpansion,然後照顧任何異常(劉海)作爲例外。

任何建議,以減少這種解決方案的醜(但不是它的速度)是非常受歡迎的。

後記--------------------

我納入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分鐘)。有時候優雅只是不如醜陋一樣快。我堅持我的原創。

+0

很好地完成。但我認爲我的最新更新請求數低於5應該勝過這個。 – dbenham

+0

另外 - 使用臨時文件的好主意。您可能會驚訝地發現臨時文件多久會打敗純粹基於內存的批處理解決方案。 – dbenham