加快文件夹重组代码

时间:2015-05-19 14:49:48

标签: batch-file

我有一些CMD代码,Rojo和Magoo帮助我编写了一个针对目录中某些XML文件的代码。代码从文件名中的文件中获取日期和时间,并从中创建年份和月份文件夹,然后将文件移入其中。我遇到的问题是文件夹本身包含914,000个xml文件,而脚本无法处理它。我需要更快的东西或多线程的脚本。我考虑的另一个选项是一次移动几千个文件,然后在临时目录中运行它们,并在脚本的最后将这些文件夹移动到生产位置。以下是创建要测试的XML文件的代码和另一个脚本。日期未经验证,但对于本练习,它们不需要。这将在Microsoft Server 2012 R2 VM上运行。 运行处理器Intel(R)Xeon(R)CPU E5-2650 0 @ 2.00GHz,2000 Mhz,1个Core(s),1个逻辑处理器和4个ram。我还包括Powershell和VbScript标签,以防有人可以提供以这些语言编写代码的任何建议。

XML移动脚本

@ECHO OFF
SETLOCAL
Title Reorganizing XMLs - DO NOT CLOSE THIS WINDOW!
color 0F
mode con: cols=100 lines=6
prompt $t $d$_$p$g

::Get start time
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"
)

Echo Start time: %start%

set "sourcedir=C:\Temp\TestDummyFiles"
set "tempdir=C:\temp\xmlreorgtemp"

::call :Get1000Files %sourcedir% %tempdir% %total%

pushd %sourcedir%
SET "spinChars=\|/-"
for /f %%a in ('"prompt $H&for %%b in (1) do rem"') do set "BS=%%a"
set "spaces=          "
SET /a filesMoved = 0, spinPos = 0, prev = 0

echo Moving XML Files...

setlocal enabledelayedexpansion
for /L %%I in (1,1,7) do set "BS=!BS!!BS!"
for /L %%I in (1,1,3) do set "spaces=!spaces!!spaces!"

For %%A in (%sourcedir%\*.xml) do set /a cnt+=1
echo.
Echo Total XML files: %cnt%
echo.

FOR /f "tokens=1*delims=" %%a IN ('dir /b /a-d "%sourcedir%\*.xml" ' ) DO (
        set /a filesmoved += 1 
        call :spinner !filesmoved! "%%~nxa"
)
call :spinner %filesMoved% Done.

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"
)

echo End time: %end%
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%
if %cc% lss 10 set cc=0%cc%
echo Elapsed Time: %hh%:%mm%:%ss%
endlocal & echo;
exit /b 0

:Get1000Files
@echo off
setlocal enabledelayedexpansion
for /f %%a in ('dir "%~1" /b /a-d *.xml') do (
    set /a cnt+=1 & move "%%~a" "%~2"
        if !cnt! EQU 1000 exit /b
)
exit /b

:spinner <filecount> <filename>
set /a spinPos += 1, spinPos %%= 4, ten = %~1 / 10 * 10
if "%~2"=="Done." set ten=%~1
set "str=[!spinChars:~%spinPos%,1!] %ten% files moved... [%~2]"
set "str=%str:~0,79%"
call :length len "%str%"
set /a diff = 79 - len
if %diff% gtr 0 set "str=%str%!spaces:~-%diff%!"
set /P "=!BS:~-79!%str%"<NUL
if "%~2" NEQ "Done." call :process %~2
exit /b 0

:length <return_var> <string>
setlocal enabledelayedexpansion
if "%~2"=="" (set ret=0) else set ret=1
set "tmpstr=%~2"
for %%I in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
        if not "!tmpstr:~%%I,1!"=="" (
                set /a ret += %%I
                set "tmpstr=!tmpstr:~%%I!"
        )
)
endlocal & set "%~1=%ret%"
exit /b 0

:process
FOR /f "tokens=2,3,6delims=_" %%m IN ("%~1") DO SET "date1=%%m"&SET "date2=%%n"&SET "whichdate=%%o"
IF DEFINED whichdate SET "date1=%date2%"
IF NOT DEFINED date2 exit /b 1
If not exist .\%date1:~0,4%\%date1:~4,2% MD .\%date1:~0,4%\%date1:~4,2%
MOVE %~1 .\%date1:~0,4%\%date1:~4,2%\ > nul

创建一些虚拟文件的脚本

@echo off
setlocal EnableDelayedExpansion

cd /d %~dp0
For /f %%a in ('copy /Z "%~dpf0" nul') Do set "CR=%%a"
set fileSize=%~Z1
set /a cnt=0
echo Creating files. Please wait.&echo.
:loop
    if %cnt% GTR 5000 exit /b
    set /a cnt+=1
    set /p "=Creating %cnt% File(s)       !CR!"<nul:
    Call :random 2009 2015 yyyy
    call :random 1 12 mm
    call :random 1 31 dd
    if %mm% LSS 10 set mm=0%mm%
    if %dd% LSS 10 set dd=0%dd%
    set /P "=0" > thisSize.txt < NUL
    (for /L %%i in (0,1,30) do (
         set /A "bit=(1<<%%i)&fileSize, fileSize&=~(1<<%%i)"
         if !bit! neq 0 type thisSize.txt
         if !fileSize! neq 0 type thisSize.txt >> thisSize.txt
    )) > IDABCDEFG001_STUFF_%yyyy%%mm%%dd%_ABC_0_1234567890.xml
    del thisSize.txt
goto :loop 
exit /b

:random Min Max [RtnVar]
    @echo off & setlocal
    set /a rtn=%random% %% ((%~2)-(%~1)+1) + (%~1)
    (endlocal
        if "%~3" neq "" (set %~3=%rtn%) else echo:%rtn%
    )
exit /b

服务器上有Powershell 4。

2 个答案:

答案 0 :(得分:2)

不是powershell,但也许这可以完成工作

@echo off
    setlocal enableextensions disabledelayedexpansion

    set "xmlFolder=C:\Temp\TestDummyFiles"

    pushd "%xmlFolder%" && (
        for %%x in ("*_*_*.xml") do if exist "%%x" (
            for /f "tokens=2-4 delims=_" %%a in ("%%~nx") do if "%%c"=="" (set "fileDate=%%a") else (set "fileDate=%%b")
            setlocal enabledelayedexpansion
            for /f "tokens=1,2" %%a in ("!fileDate:~0,4! !fileDate:~4,2!") do (
                endlocal
                <nul set /p "=%%a\%%b : "
                md ".\%%a\%%b" 2>nul 
                move /y "*_%%a%%b??_*.xml" ".\%%a\%%b" 2>nul | find /v ":"
            )
        )
        popd
    )

您的代码速度有三个原因(因为您正在处理914000个文件):

  1. 有914000 !!文件
  2. call用法很慢。每个文件914000 * #calls =非常慢
  3. 对控制台的914000状态更新很慢
  4. for /f
  5. 是的,

    中使用的for /f命令
    FOR /f "tokens=1*delims=" %%a IN ('dir /b /a-d "%sourcedir%\*.xml" ' ) DO (
    for /f %%a in ('dir "%~1" /b /a-d *.xml') do (
    

    有一个问题,因为:

    1. dir命令必须枚举914000个文件
    2. 在开始处理之前,需要将完整列表加载到内存中
    3. for /f命令将数据加载到缓冲区。当缓冲区已满时,将创建一个新的更大(在Windows 7中增加4KB)缓冲区,并将数据从旧缓冲区复制到新缓冲区,并重复此过程,直到检索到所有数据。每次调整缓冲区大小时,都需要进行更大的内存复制操作,以便处理所有数据所需的时间呈指数级增长。
    4. 这意味着

      914000 files * ( 50 chars file name + CR LF ) = 47528000 characters
      47528000 characters / 4KB buffer increase = 11603 redim operations
      11603 redim operations = 1103170928640 bytes moved in memory copy operations
      

      要处理所有这些,建议的代码将

      1. 使用简单的for枚举文件。该过程从找到的第一个文件开始,并且在迭代文件时完成更多搜索操作。

      2. 不是处理每个文件,而是仅在一个move操作中移动与日期匹配的所有文件。

答案 1 :(得分:2)

如果您有大量文件,则可以按最小数量的组对其处理进行重新排序。在您的示例代码中,您创建了5000个虚拟文件,但仅在6年内。下面的代码按年处理文件,然后是月份:

@ECHO OFF
SETLOCAL EnableDelayedExpansion

set "sourcedir=C:\Temp\TestDummyFiles"
pushd %sourcedir%

:nextYear
for %%a in (*.xml) do set "fileName=%%a" & goto break
:break
if not defined fileName goto :EOF

FOR /f "tokens=2,3,6 delims=_" %%m IN ("%fileName%") DO SET "date1=%%m" & SET "date2=%%n" & SET "whichdate=%%o"
IF DEFINED whichdate SET "date1=%date2%"
IF NOT DEFINED date2 exit /b 1

set "YYYY=%date1:~0,4%"
set "MM=100"
for /L %%m in (1,1,12) do (
    set /A MM+=1
    MD "%YYYY%\!MM:~1!" 2> NUL
    MOVE "*_%YYYY%!MM:~1!??_*.xml" "%YYYY%\!MM:~1!"
)
goto nextYear

在我看来,你应该从问题的描述开始这个主题,比如&#34;我有914,000个这种格式的文件IDABCDEFG001_STUFF_yyyymmdd_ABC_0_1234567890.xml我希望将它们移动到yyyy \ mm结构的文件夹&# 34 ;.我真的不想尝试了解阅读代码问题的细节。我不明白你的代码是为了从文件名中获取日期,所以我只是复制了它......