0

我需要创建一个 vbs 以在具有子文件夹的文件夹中按修改日期对可设置数量的文件(仅文件)进行排序,并使用绝对路径打印文件,如下所示:

VB:

Dim MAX
Dim Folder
MAX = 100
Folder = "C:\Test"
vbscript functions to group all files of all subfolders, and sort them by MOD date... ok
vbscript funciont to make a text file output (This i can't do it by myself)
end

文本文件输出(100 个最新文件):

c:\newest 1st file.txt
c:\subfolder1\newest 2nd file.txt
c:\subfolder7\newest 3rd file.txt
c:\subfolder2\newest 4 file.txt
c:\subfolder8\newest 5 file.txt
c:\subfolder4\newest 6 file.txt
c:\subfolder2\newest 7 file.txt
c:\newest 8 file.txt
c:\subfolder3\newest 9 file.txt
etc...

真的不管解决方案是否可以用批处理来完成,我同意,但我试过这个:

Dir /S /TC /O-D

唯一的问题是不要告诉我绝对路径......

编辑:哦,当然我试过了:

Dir / B / S / TC / O-D

但是 /B 参数意味着我之前所说的命令有很大的不同......

我是说:

Dir / S / TC / O-D

该命令将所有子目录中的所有文件(一起)分组并按日期对它们进行排序。(好的!)

Dir / B / S / TC / O-D

该命令逐个文件夹处理并排序每个文件并显示它。(坏的!)

所以,如果我只需要对 neswest 100 个文件进行排序,并且如果我使用带有“/B”参数的批处理 dir 命令,我会得到这个:

输出:

(Position 1) c:\subfolder1\Newest 1st file of this folder.txt
(Position 2) c:\subfolder1\Newest 2nd fil eof this folder.txt
(Position 3) c:\subfolder1\Old file of this folder.txt
(Position 3) c:\subfolder1\Older file of this folder.txt
(Position 4) c:\subfolder1\Oldest file of this folder.txt
(Position 5) c:\subfolder2\Newest 1st file of this folder.txt
(Position 6) c:\subfolder2\Newest 2nd file of this folder.txt
(Position 7) c:\subfolder2\Old file.txt
etc ...

所以请不要告诉我任何关于使用 /B 参数的 dir,我知道这很好:(。

再次感谢

4

4 回答 4

2

这是一个仅使用本机命令的纯批处理解决方案 - 它实际上表现良好:-)

我不确定在 WMIC 命令中是否可能需要转义其他字符,因为我对 WMIC 没有太多经验。但除此之外,我认为这是相当防弹的。

::treeNewestFiles FileCount [RootFolder]
::
::  Searches the directory tree rooted at RootFolder and prints
::  the most recently modified files. The number of files printed
::  is limited to a maximum of FileCount. If RootFolder is not
::  specified then the root is the current directory.
::
@echo off
setlocal disableDelayedExpansion

::define base temp file name
set "tempFile=%temp%\fileDates%random%.txt"

::Loop through all folders rooted at %2 (current directory if not specified), and use
::WMIC to list last modified timestamp and full path of each file in each folder.
::The last modified timestamp is in UTC ISO 8601 format, so it sorts properly.
(
  for /r %2 %%F in (.) do (
    set "folder=%%~pnxF"
    set "drive=%%~dF"
    setlocal enableDelayedExpansion
    2>nul wmic datafile where (path='!folder:\^=\\!\\' and drive='%%~dF'^) get lastmodified, name
    endlocal
  )
)>"%tempFile%"

::Convert unicode to ansii
type "%tempFile%" >"%tempFile%2"

::Preserve only data rows
findstr "^[0-9]" "%tempFile%2" >"%tempFile%3"

::Sort the results in descending order
sort /r "%tempFile%3" >"%tempFile%4"

::Print first %1 files in results
set n=0
for /f "usebackq tokens=1*" %%A in ("%tempFile%4") do (
  echo %%B
  set /a "n+=1, 1/(%1-n)" 2>nul || goto finish
)

:finish
del "%tempFile%*"

新的更快的版本

我的原始代码为每个目录重新调用了 WMIC。WMIC 每次调用都需要花费大量时间进行初始化。通过构建命令脚本并仅调用一次 WMIC,我将执行时间减少了 45%。性能增益量是树中目录数量的函数。目录数量越多,这个新版本的帮助就越大。我确信通过转换为 VBS 可以获得更多的性能提升,但我认为这不值得付出努力。我相信这个过程现在已经非常优化了。

::treeNewestFiles FileCount [RootFolder]
::
::  Searches the directory tree rooted at RootFolder and prints
::  the most recently modified files. The number of files printed
::  is limited to a maximum of FileCount. If RootFolder is not
::  specified then the root is the current directory.
::
@echo off
setlocal disableDelayedExpansion

::define temp folder for temp files
set "tempFolder=%temp%\fildates%random%"
md "%tempFolder%"

::define base path\name for temp files
set "tempFile=%tempFolder%\tempFile.txt"

::Loop through all folders rooted at %2 (current directory if not specified),
::and build a script of WMIC commands that will list last modified timestamps
::and full path of files for each folder. The last modified tamestamp will
::be in ISO 8601 format, so it sorts properly.
(
  echo /append:"%tempFile%1"
  for /r %2 %%F in (.) do (
    set "folder=%%~pnxF"
    set "drive=%%~dF"
    setlocal enableDelayedExpansion
    echo datafile where (path='!folder:\^=\\!\\' and drive='%%~dF'^) get lastmodified, name
    endlocal
  )
  echo quit
)>"%tempFile%"

::Execute the WMIC script
::WMIC creates a temporary file in current directory,
::so change directory 1st so it doesn't interfere with results.
pushd "%tempFolder%"
cmd /c ^<"%tempFile%" wmic ^>nul 2^>nul

::Convert unicode to ansii
type "%tempFile%1" >"%tempFile%2"

::Preserve only data rows
findstr "^[0-9]" "%tempFile%2" >"%tempFile%3"

::Sort the results in descending order
sort /r "%tempFile%3" >"%tempFile%4"

::Print first %1 files in results
set n=0
for /f "usebackq tokens=1*" %%A in ("%tempFile%4") do (
  echo %%B
  set /a "n+=1, 1/(%1-n)" 2>nul || goto finish
)

:finish
popd
rd /q /s "%tempFolder%"
于 2012-04-19T03:54:02.640 回答
2

我遵循上面 KH1 的建议:“您需要将所有文件路径和名称与 DateModified 一起加载到一个数组中,对数组进行排序,然后遍历数组并输出文件路径和名称”,但是在批处理文件。下面的程序使用文件的 YYYYMMDDHHMM 修改时间戳作为数组的索引。通过这种方式,数组保持由 Batch SET 命令自动排序。参数与上述 dbenham 程序相同:FileCount 和可选的 RootFolder。

@echo off
setlocal EnableDelayedExpansion

rem Get order of FileTimeStamp elements independent of regional settings
for /F "skip=1 tokens=2-4 delims=(-)" %%a in ('date^<NUL') do (
   set timeStampOrder=%%a %%b %%c ho mi ap
)

rem For each file in the folder given by %2 (default current one)
for /R %2 %%F in (*.*) do (
   rem Extract FileTimeStamp data (yy mm dd ho mi ap)
   for /F "tokens=1-6" %%a in ("%timeStampOrder%") do (
      for /F "tokens=1-6 delims=/-.: " %%i in ("%%~tF") do (
         set %%a=%%i
         set %%b=%%j
         set %%c=%%k
         set %%d=%%l
         set %%e=%%m
         set %%f=%%n
      )
   )
   rem Adjust hour if needed
   if !ap! equ p set /A "ho=10!ho! %% 100 + 12
   rem Create the array element with proper index
   set "file[!yy!!mm!!dd!!ho!!mi!]=%%~fF"
)

rem At this point the array is automatically sorted

rem Show the first %1 array elements
set n=0
for /F "tokens=2 delims==" %%a in ('set file[') do (
   echo %%a
   set /A n+=1
   if !n! equ %1 goto finish
)

:finish
于 2012-04-19T05:48:55.367 回答
1

让您入门的三步演示:

Option Explicit

' ADO Constants needed in this demo
Const adDBTimeStamp      =        135 ' 00000087
Const adVarWChar         =        202 ' 000000CA
Const adClipString       =          2 ' 00000002

' Globals
Dim goFS   : Set goFS = CreateObject("Scripting.FileSystemObject")
Dim gsSDir : gsSDir   = "..."

' Dispatch using comments or re-order
WScript.Quit demoTraversal()
WScript.Quit demoDirWalker()
WScript.Quit demoAdoDirWalker()

' Step00: Understanding recursive traversal
Function demoTraversal()
  walkDir00 goFS.GetFolder(gsSDir)
End Function ' demoTraversal

' Minimal recursive traversal: do something for each file in folder and
' then call same Sub for each subfolder
Sub walkDir00(oDir)
  Dim oElm
  For Each oElm In oDir.Files
      WScript.Echo oElm.DateLastModified, oElm.Path
  Next
  For Each oElm In oDir.SubFolders
      walkDir00 oElm
  Next
End Sub ' walkDir00

' Step01: Recursive traversal with Class
' Use an object to abstract the 'something' action(s) and to augment
' state (count)
Function demoDirWalker()
  Dim oDirWalker : Set oDirWalker = New cDirWalker01.init()
  walkDir01 goFS.GetFolder(gsSDir), oDirWalker
  WScript.Echo oDirWalker.Count, "files seen"
End Function ' demoTraversal

Class cDirWalker01
  Private m_nCount
  Public Function init()
    Set init    = Me
    m_nCount    = 0
  End Function ' init
  Public Sub processFile(oFile)
    ' add bool expression or function to filter
    WScript.Echo oFile.DateLastModified, oFile.Path
    m_nCount = m_nCount + 1
  End Sub ' processFile
  Public Property Get Count()
    Count = m_nCount
  End Property ' Count
End Class ' cDirWalker01

Sub walkDir01(oDir, oDirWalker)
  Dim oElm
  For Each oElm In oDir.Files
      oDirWalker.processFile oElm
  Next
  For Each oElm In oDir.SubFolders
    ' add bool expression or DirWalker.method to filter
      walkDir01 oElm, oDirWalker
  Next
End Sub ' walkDir00

' Step02: Solution (POC)
Function demoAdoDirWalker()
  Dim oDirWalker : Set oDirWalker = New cAdoDirWalker.init()
  walkDir01 goFS.GetFolder(gsSDir), oDirWalker
  oDirWalker.sort "sPath ASC, dtLM ASC"
  WScript.Echo oDirWalker.getResultString()
  oDirWalker.sort "dtLM DESC, sPath ASC"
  WScript.Echo oDirWalker.getResultString()
End Function ' demoAdoDirWalker

Class cAdoDirWalker
  Private m_oRS
  Public Function init()
    Set init  = Me
    Set m_oRS = CreateObject("ADODB.Recordset")
    m_oRS.Fields.Append "dtLM" , adDBTimeStamp
    m_oRS.Fields.Append "sPath", adVarWChar, 255
    m_oRS.Open
  End Function ' init
  Public Sub processFile(oFile)
    m_oRS.AddNew
    m_oRS.Fields("sPath").Value = oFile.Path
    m_oRS.Fields("dtLM" ).Value = oFile.DateLastModified
    m_oRS.Update
  End Sub ' add
  Public Sub sort(sWhat)
    m_oRS.sort = sWhat
  End Sub ' sort
  Public Function GetResultString()
    m_oRS.MoveFirst
    GetResultString = m_oRS.GetString(adClipString, , " | ", vbCrLf, "NULL")
  End Function ' GetResultString
End Class ' cAdoDirWalker

主要思想是使用断开连接的 ADO 记录集来存储和排序文件夹树中的文件集合。

于 2012-04-18T13:53:09.133 回答
1

您可以使用for命令和 ~ 语法来完成它(请参阅 参考资料for /?):

(for /r %A in (*) do @echo %~tA %A ) | sort /r

括号的使用允许单个重定向 od whole forto sort。没有括号 eachecho将被重定向到 individual sort,因此不会进行排序。

编辑:正如 Ekkehard.Horner 指出的那样,上述代码仅适用于日期以 yyyy-mm-dd 格式打印的区域。在以 mm/dd/yyyy 格式打印日期的区域中,您可以使用以下批处理文件:

@Echo Off
setlocal enabledelayedexpansion

if "%1"=="list" goto :list
%0 list | sort /r

endlocal
goto :EOF

:list
for /r %%A in (*) do (
  set t=%%~tA
  echo !t:~6,4!-!t:~0,2!-!t:~3,2! %%A
)
goto :EOF

我没有设法在批处理文件中使用括号重复该技巧,因此脚本使用参数调用自身,使其打印文件列表,然后对输出进行排序。通过使用%variable~:start-length%语法(请参阅set /?)和延迟变量扩展将日期转换为 yyyy-mm-dd 格式。它不像 dbenham 的解决方案那样防弹,但它确实有效。

于 2012-04-18T18:00:46.483 回答