1

私はdosバッチスクリプトの初心者です。いくつかのコード スニペットをつなぎ合わせ、いくつかの変更を加えることで、スクリプトを小さなディレクトリで動作させることができました。ディレクトリ ツリーを再帰し、パラメーターとして渡された日付範囲内のファイル数をカウントし、出力ファイル レポートを書き込みます。

小さなディレクトリ構造では問題なく動作しますが、数百を超えるフォルダーを再帰する必要がある場合は、「バッチ再帰がスタック制限を超えています」というエラーで中止されます。

このサイトから、再帰ループはあまり効率的ではないことを理解しています。スタックに一定量のデータを配置したら、乾杯します。私はこのサイトや他の場所で助けを求めました。アドバイスのほとんどは、より効率的なプログラムを作成することですが、その方法については確信が持てません。どんな助けでも大歓迎です。これを必要とするディレクトリ構造には何千ものフォルダがあるため、効率を桁違いに高める必要があります。これが私のコードです:

@echo off
setlocal enableDelayedExpansion
pushd %1

REM This program takes three parameters <starting directory> <startdate> <enddate>
REM The startdate and endate should be in format: mm/dd/yyyy
REM The program will recursively look through all directories and sub-directories
REM from the given <starting directory> and count up the number of files written
REM within the date range from <startdate> until <endate>
REM It will then write out a RetentionReport_<date>_<time>.txt file that lists
REM one line showing the <startdate> <enddate> and the # of files found.

REM If you don't pass in all three arguments it will let you know and then exit.

REM You need to set your TESTDIR below to a hardpath location for the writing
REM of temporary files and writing of the Reports

REM There is one .tmp file created during processing and then deleted.
REM To prevent the .tmp file being deleted you can comment out the second
REM instance of this line by adding a REM in front of it:
REM if exist %TESTDIR%*.tmp del %TESTDIR%*.tmp

REM If you want to print out a .tmp file that lists the files counted for the
REM period given then you could remove the REM from in front of the following
REM line in the below code: echo %%~tF   %%F>> %TESTDIR%MONTH_files.tmp


set "TAB=   "
set "MONTHTOTAL=0"
set hr=%time:~0,2%
if "%hr:~0,1%" equ " " set hr=0%hr:~1,1%
set "TESTDIR=C:\TEST\"

if "%~2" == "" (
    echo Please pass in arguments for starting directory, startdate, and enddate.
    echo startdate and endate should be in the format mm/dd/yyyy
    exit/b
)
if "%~3" == "" (
    echo Please pass in arguments for starting directory, startdate, and enddate.
    echo startdate and endate should be in the format mm/dd/yyyy
    exit/b
)


set "startdate=%~2"
set "enddate=%~3"

if exist %TESTDIR%*.tmp del %TESTDIR%*.tmp
call :run >nul
FOR /F %%i IN (%TESTDIR%temp_TotalByDir.tmp) DO set /a MONTHTOTAL=!MONTHTOTAL!+%%i
echo %startdate%%TAB%%enddate%%TAB%!MONTHTOTAL! >> %TESTDIR%RetentionReport_%date:~-    4,4%%date:~-10,2%%date:~-7,2%_%hr%%time:~3,2%%time:~6,2%.txt
if exist %TESTDIR%*.tmp del %TESTDIR%*.tmp
exit /b

:run
for %%F in (.) do echo %%~fF
endlocal

:listFolder
setlocal enableDelayedExpansion
set "ADD=0"

for %%F in (*) do ( 
  if %%~tF GEQ %startdate% (
      if %%~tF LEQ %enddate% (
          REM echo %%~tF   %%F>> %TESTDIR%MONTH_files.tmp
      set /a ADD+=1
      )
  )
)

echo !ADD! >> %TESTDIR%temp_TotalByDir.tmp

for /d %%F in (*) do (
  pushd "%%F"
  call :listFolder
  popd
)

endlocal

exit /b

よろしくお願いします!!!

4

3 に答える 3

0

フォルダの数は、再帰レベルとは関係ありません。各ディレクトリ レベルが再帰呼び出しで処理される場合、再帰呼び出しの最大深さは、非常に大きなツリーで 10 または 11 にする必要があります。以下のコードをベースとして開始し、必要に応じて変更することをお勧めします。

@echo off
call :treeProcess
goto :eof

:treeProcess
rem Do whatever you want here over the files of this subdir, for example:
copy *.* C:\dest\dir
for /D %%d in (*) do (
    cd %%d
    call :treeProcess
    cd ..
)
exit /b

編集

私はあなたのコードを見直しましたが、唯一の奇妙な点は、ラベルのendlocal下に配置されたコマンドです。:runこの時点で、以前に定義されたすべての変数が解放され、予測できない結果になると思います。これは、正しく機能すると思われるコードのより単純なバージョンです。

@echo off
setlocal

set "TAB=   "
set "MONTHTOTAL=0"
set hr=%time:~0,2%
if "%hr:~0,1%" equ " " set hr=0%hr:~1,1%
set "TESTDIR=C:\TEST\"

set "startdate=%~2"
set "enddate=%~3"

if exist %TESTDIR%*.tmp del %TESTDIR%*.tmp
call :run >nul
FOR /F %%i IN (%TESTDIR%temp_TotalByDir.tmp) DO set /a MONTHTOTAL+=%%i
echo %startdate%%TAB%%enddate%%TAB%%MONTHTOTAL% >> %TESTDIR%RetentionReport_%date:~-4,4%%date:~-10,2%%date:~-7,2%_%hr%%time:~3,2%%time:~6,2%.txt
if exist %TESTDIR%*.tmp del %TESTDIR%*.tmp
exit /b

:run

:listFolder

set "ADD=0"

for %%F in (*) do ( 
   if %%~tF GEQ %startdate% (
      if %%~tF LEQ %enddate% (
         REM echo %%~tF   %%F>> %TESTDIR%MONTH_files.tmp
         set /a ADD+=1
      )
   )
)

echo %ADD% >> %TESTDIR%temp_TotalByDir.tmp

for /d %%F in (*) do (
  cd "%%F"
  call :listFolder
  cd ..
)
exit /b
于 2014-02-28T17:56:46.970 に答える
0

:listFolderあなたの問題は、内で呼び出すことによって引き起こされます:listFolder

@echo off
setlocal

REM This program takes three parameters <starting directory> <startdate> <enddate>
REM The startdate and endate should be in format: mm/dd/yyyy
REM The program will recursively look through all directories and sub-directories
REM from the given <starting directory> and count up the number of files written
REM within the date range from <startdate> until <endate>
REM It will then write out a RetentionReport_<date>_<time>.txt file that lists
REM one line showing the <startdate> <enddate> and the # of files found.

REM If you don't pass in all three arguments it will let you know and then exit.

REM You need to set your TESTDIR below to a hardpath location for the writing
REM of temporary files and writing of the Reports

REM There is one .tmp file created during processing and then deleted.
REM To prevent the .tmp file being deleted you can comment out the second
REM instance of this line by adding a REM in front of it:
REM if exist %TESTDIR%*.tmp del %TESTDIR%*.tmp

REM If you want to print out a .tmp file that lists the files counted for the
REM period given then you could remove the REM from in front of the following
REM line in the below code: echo %%~tF   %%F>> %TESTDIR%MONTH_files.tmp


set "startdate=%~2"
set "enddate=%~3"
IF DEFINED startdate IF DEFINED enddate GOTO parmsok
echo Please pass in arguments for starting directory, startdate, and enddate.
echo startdate and endate should be in the format mm/dd/yyyy
GOTO :eof

:parmsok
CALL :convdate startdate "%startdate%"
CALL :convdate enddate "%enddate%"
set "TAB=   "
set /a MONTHTOTAL=0
set /a GRANDTOTAL=0
set "TESTDIR=C:\TEST\"

if exist %TESTDIR%*.tmp del %TESTDIR%*.tmp
pushd %1
REM call :run >NUL
call :run
popd
FOR /F %%i IN (%TESTDIR%temp_TotalByDir.tmp) DO set /a MONTHTOTAL+=%%i
set hr=%time:~0,2%
set hr=%hr: =0%
echo %startdate%%TAB%%enddate%%TAB%%MONTHTOTAL% >> "%TESTDIR%RetentionReport_%date:~-4,4%%date:~-10,2%%date:~-7,2%_%hr%%time:~3,2%%time:~6,2%.txt"
ECHO %grandtotal%
if exist %TESTDIR%*.tmp del %TESTDIR%*.tmp
GOTO :eof 

:run
for /d /r . %%T in (.) do (
  pushd "%%T"
  IF NOT ERRORLEVEL 1 (
   call :listFolder
   POPD
  )
)
GOTO :eof

:listFolder
set /a ADD=0

for %%F in (*) do (
  CALL :convdate filedate "%%~tF"
  CALL :compdate
  IF DEFINED inrange (
  REM  echo %%~tF   %%F>> %TESTDIR%MONTH_files.txt
    set /a ADD+=1
    SET /a GRANDTOTAL+=1
  )
)
echo %ADD% >> %TESTDIR%temp_TotalByDir.tmp

GOTO :EOF

:: Convert date in %2 to yyyymmdd in %1

:convdate
SET "$1=%~2"
:: replace any space with 0
SET $1=%$1: =0%
:: convert date format. I use dd/mm/yyyy.
SET %1=%$1:~6,4%%$1:~3,2%%$1:~0,2%
:: version for mm/dd/yyyy.
REM SET %1=%$1:~3,2%%$1:~6,4%%$1:~0,2%
GOTO :EOF

:: Set Inrange iff date is in range

:compdate
SET "inrange="
if %filedate% GEQ %startdate% if %filedate% LEQ %enddate% SET inrange=Y
GOTO :eof

面白いエクササイズ。

いくつかのポイント:

遅延拡張は必要ありません。

日付範囲変数を割り当てます。両方が存在する場合のみ続行し、そうでない場合はエラー メッセージを表示して終了します。

共通変数を設定します。構文set "var=string"は、行の末尾のスペースが割り当てられた値に含まれないように設計されていることに注意してください。set /a数値を割り当てる構文は、末尾のスペースの影響を受けません。

PUSHDターゲット ディレクトリ。POPD加工後は元に戻ります。

kbd 割り込みを許可するために>nulからを削除しましたcall :run

構文 `set "var=%var: =0%" は、スペースをゼロに置き換えます。

ランタイムによって時間が変更された場合に備えて、 の設定をが生成さhrれる直前に移動しました。RetentionReport

.tmp元の(オプションの)ファイルリストは、削除されるはずのファイルに書き込まれていたため、.txtファイルに変更しました。

for /d /r示されている形式で現在のディレクトリを含めて、再帰的なディレクトリ名スキャンを実行します。

失敗した場合( withpushdを含むディレクトリ名で実行した場合)、効果がないため、is 0 の場合にのみandを実行する必要があります。!delayedexpansionerrorlevelcallpopd

listfolder は、現在のディレクトリを 1 つだけ調べます。

適切に比較するには、日付を yyyymmdd 形式に変換する必要があります。これは、提供された日付に対して最初に一度だけ実行する必要があります (ループ内で永続的に再変換しても意味がありません)。

for の主な用途delayedexpansionは、ループ内で値が変化する変数の値にアクセスすることです。ただし、if definedテスト (およびif existおよびif errorlevel n) は、解析時の値ではなく、実行時の値に作用します。したがって、このメソッドはdelayedexpansion、デモンストレーションのために - を使用することを避けます。

最後に:xcopy /L /D適切な日付を指定して空のディレクトリに対して を実行すると、選択した日付以降に生成されたファイルが一覧表示されます。したがって、XCOPY /L /D2 つの日付で空のディレクトリを実行すると、ファイル数が生成される可能性があります。#早く - #後で = #間。

のファイル数 (= 行数)は(および)MONTH_files.txtと等しくなりますが、最初に削除することをお勧めします(もはやファイルではありません...)GRANDTOTALMONTHTOTALMONTH_files.txt.tmp

于 2014-02-28T19:03:36.837 に答える
0

再帰スタックの制限以外にも問題があります。IF ステートメントは、日付を比較する方法を知りません。文字列と数字しか認識しません。日付を正しく比較するには、日付を YYYYMMDD 形式に再フォーマットする必要があります。

出力ファイル名に使用するタイムスタンプ情報は、一度に収集する必要があります。既存のコードは、プロセスの開始時に時間を取得し、プロセスの終了時に日付、分、秒を取得します。良くない。開始時間と終了時間の間にかなりの時間差が生じる可能性があります。

Batch には、次の 2 種類の再帰エラーがあります。

1) 1 つの CALL レベル内に 31 の SETLOCAL レベルのみ。

2) Windows のバージョン、マシンのメモリなどに応じて、許可される再帰呼び出しの数は可変です。

スタックのサイズを増やす方法はありません。再帰エラーが発生した場合は、再帰の量を減らす方法を探す必要があります。あなたの場合、単純に FOR /R にすべての再帰を任せることができます!

4 番目の引数が渡された場合にファイルのリストが生成されるように、スクリプトを変更しました。また、タイムスタンプをファイルリストのファイル名に組み込みました。

このコードは、マシンのファイルの日付/時刻の値が MM/DD/YYYY で始まると想定しています。そうでない場合は、コードを変更する必要があります。

@echo off
setlocal enableDelayedExpansion

REM This program takes three parameters <starting directory> <startdate> <enddate>
REM The startdate and endate should be in format: mm/dd/yyyy
REM The program will recursively look through all directories and sub-directories
REM from the given <starting directory> and count up the number of files written
REM within the date range from <startdate> until <endate>
REM It will then write out a RetentionReport_<date>_<time>.txt file that lists
REM one line showing the <startdate> <enddate> and the # of files found.

REM If you don't pass in all three arguments it will let you know and then exit.

REM You need to set your TESTDIR below to a hardpath location for the writing
REM of the Reports

REM If you want to print out a .tmp file that lists the files counted for the
REM period given then you can pass in a 4th argument with any value

if "%~3" == "" (
  echo Please pass in arguments for starting directory, startdate, and enddate.
  echo startdate and endate should be in the format mm/dd/yyyy
  exit/b
)

set "TAB=   "
set "TESTDIR=D:\TEST\"

set "startdate=%~2"
set "start=%startdate:~-4%%startdate:~0,2%%startdate:~3,2%"
set "enddate=%~3"
set "end=%enddate:~-4%%enddate:~0,2%%enddate:~3,2%"

set "timestamp=%date:~-4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2%%time:~6,2%"
set "timestamp=%timestamp: =0%"

for /r "%~1" %%F in (*) do (
  set "dt=%%~tF"
  set "dt=!dt:~6,4!!dt:~0,2!!dt:~3,2!"
  if !dt! geq %start% if !dt! leq %end% (
    if "%~4" neq "" (echo %%~tF   %%F) >>"%TESTDIR%MONTH_files_%timestamp%.tmp"
    set /a cnt+=1
  )
)

(echo %startdate%%TAB%%enddate%%TAB%%cnt%) >>"%testdir%RetentionReport_%timestamp%.txt"
于 2014-02-28T20:24:55.237 に答える