windows 在批处理文件中查找文件是否超过4小时

jgwigjjp  于 2023-04-22  发布在  Windows
关注(0)|答案(4)|浏览(126)

很简单,我怎么知道文件夹中的特定文件是否超过X小时?文件名和位置将始终相同。
谢谢

py49o6xq

py49o6xq1#

将文件时间与批次代码进行比较时,必须考虑以下因素:
1.日期和时间格式设置
1.存储介质的文件系统
1.时区,夏令时
1.自动调整夏令时

  1. Windows版本

1.日期时间格式设置

有环境变量DATETIME,它们在访问时(而不是在开始批处理执行时)返回当前日期和时间,其格式分别取决于Windows Language & region settingsWindows Region and Language settings。国家/地区定义日期和时间格式。
甚至可以使用DD.MM.YYYY定义德语短日期格式(日和月,前导为零)和时间格式,HH:mm:ss(24小时格式,以前导零表示小时,分钟和秒)。但这并不意味着DATETIME的值真的使用这种格式。例如TIME当选择德国国家时,即使定义了HH:mm:ss,其格式也为H:mm:ss,ms,这意味着小时没有前导零,但分钟和秒,以及时间字符串中逗号后的毫秒。日期字符串也可以根据地区设置带有或不带有工作日。
还有一种模式%~t可以获取目录或文件的最后修改日期和时间。在命令提示符窗口call /?for /?中运行,并阅读为这两个命令显示的所有帮助页面以获取详细信息。%~t返回的日期和时间字符串的格式以及在命令DIR的输出上显示的格式也取决于Windows区域设置,但通常不等于变量DATETIME的格式。文件或目录的最后修改时间的时间通常在日期/时间字符串中没有第二个值。
最好是找出以下批处理文件最好在上午10:00之前执行当前计算机上的当前用户的格式。

@echo off
echo Current date/time: %DATE% %TIME%
for %%I in ("%~f0") do echo Batch file time:   %%~tI
pause

2.存储介质文件系统

在Windows文件系统中,file times使用两种存储格式:

  • 在使用NTFS的存储介质上,使用NTFS precision time,它是自1601年1月1日上午12:00协调世界时(UTC)以来经过的100纳秒间隔的64位数。
  • 在使用FAT、FAT 16、FAT32或exFAT的存储介质上,文件时间使用DOS Date Time存储,该格式具有两个16位值,分辨率为2秒,并使用当前本地时间。

这意味着奇数秒对于FAT媒体上的文件是不可能的,但在NTFS媒体上。将NTFS驱动器上的文件与上次修改时间的奇数秒复制到FAT32驱动器会导致相同的文件与上次修改时间相差1秒。
NTFS媒体上的文件时间以UTC存储。%~t或命令DIR返回的内容以及在Windows资源管理器中显示的内容取决于

  • 当前时区,
  • 该时区的夏令时,
  • 如果当前时间在夏令时周期内,
  • 以及当前是否启用夏令时自动调整。

更改这4个参数中的任何一个都会立即更改NTFS存储介质上文件和目录的所有显示文件时间。
FAT媒体上的文件时间或多或少是恒定的,因为在创建,修改或最后访问时使用本地时间,但请进一步阅读以了解详细信息。

3.时区、夏令时

由于NTFS媒体上的文件时间以UTC存储,但以本地时间显示,因此当前时区和该时区的夏令时对于文件时间比较非常重要,特别是对NTFS和FAT32驱动器上的文件进行比较时。
时区和夏令时设置可能很重要,具体取决于Windows的版本,对于FAT媒体上的文件也是如此。请阅读下面的详细信息。

4.自动调整夏令时

有自动调整夏时制的设置,当默认启用时变得很重要,当前设置的时区定义了夏时制的调整。
例如,夏令时目前在欧洲处于活动状态。在Windows的时钟设置中禁用此选项会导致NTFS媒体上显示的文件时间立即更改-1小时,并且根据Windows的版本,FAT媒体上的许多文件也会立即更改。
在比较NTFS和FAT媒体上的文件时间时,必须考虑夏令时调整导致的1小时时差。

5. Windows版本

所有Windows NT版本都支持NTFS。在支持NTFS的所有Windows版本上,文件和目录的文件时间行为是相同的。
但是如何解释FAT媒体上的文件时间取决于Windows的版本。在Windows Vista之前,FAT媒体上的文件时间独立于时区的更改,日光节约时间设置和日光节约时间的自动调整。文件的最后修改日期/时间在Windows 2000和Windows XP上是恒定的。

但在Windows Vista及更高版本上,FAT媒体上显示的文件时间取决于夏时制和自动调整夏时制。时区并不直接重要。选择当前使用的另一个时区不会改变FAT媒体上文件的显示时间,因为它会改变NTFS媒体上文件的显示时间。但是如果当前时间在活动时区的夏时制时间段内并且启用了夏时制时间的自动调整并且文件或目录的本地时间在夏时制时间段内,+1小时由Windows Vista和更高版本添加。在标准时间内最后修改FAT驱动器上的文件在一年中是恒定的;仅显示在DST期间内最后修改的文件,根据启用自动DST调整的当前时间,有或没有+1小时。
例如,当在Windows 7机器上的FAT32驱动器上的文件夹的公共共享并使用Windows XP机器连接到此公共文件夹时,不同的FAT文件时间管理可能会非常混乱。最后修改于2015-09-19 17:39:08存储在Windows 7 PC上的FAT32驱动器上的文件今天由Windows 7显示,文件时间为德国时区19.09.2015 17:39:08,但在2个月内,尽管没有修改为19.09.2015 16:39:08,Windows XP在连接的Windows 7 PC上显示相同的文件,并且在未来保持文件时间19.09.2015 17:39:08不变。
将存储在归档文件(ZIP,RAR,...)中的文件与NTFS或FAT媒体上的文件进行文件时间比较可能真的是一场噩梦。

6.文件时间与当前时间比较

对于此任务,将文件的上次修改时间与当前时间进行比较,只需考虑日期和时间格式设置就足够了。
下面的批处理代码使用了我在Batch file to delete files older than N days上的回答中详细解释的代码。在使用下面的代码之前,应该阅读这个解释。
根据变量DATETIME的日期/时间格式以及%~tI为本地Windows PC上的文件返回的日期/时间字符串,可能需要对代码进行小的修改。在批处理代码的注解行中给出了适当的提示。

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem Get seconds since 1970-01-01 for current date and time.
rem From date string only the last 10 characters are passed to GetSeconds
rem which results in passing dd/mm/yyyy or dd.mm.yyyy in expected format
rem to this subroutine independent on date string of environment variable
rem DATE is with or without abbreviated weekday at beginning.
call :GetSeconds "%DATE:~-10% %TIME%"

rem Subtract seconds for 4 hours (4 * 3600 seconds) from seconds value.
set /A "CompareTime=Seconds-4*3600"

rem Define batch file itself as the file to compare time by default.
set "FileToCompareTime=%~f0"

rem If batch file is started with a parameter and the parameter
rem specifies an existing file (or directory), compare with last
rem modification date of this file.
if not "%~1" == "" if exist "%~1" set "FileToCompareTime=%~1"

rem Get seconds value for the specified file.
for %%F in ("%FileToCompareTime%") do call :GetSeconds "%%~tF:0"

rem Compare the two seconds values.
if %Seconds% LSS %CompareTime% (
    echo File %FileToCompareTime% was last modified for more than 4 hours.
) else (
    echo File %FileToCompareTime% was last modified within the last 4 hours.
)
endlocal
goto :EOF

rem No validation is made for best performance. So make sure that date
rem and hour in string is in a format supported by the code below like
rem MM/DD/YYYY hh:mm:ss or M/D/YYYY h:m:s for English US date/time.

:GetSeconds
rem If there is " AM" or " PM" in time string because of using 12 hour
rem time format, remove those 2 strings and in case of " PM" remember
rem that 12 hours must be added to the hour depending on hour value.
set "DateTime=%~1"
set "Add12Hours=0"
if not "%DateTime: AM=%" == "%DateTime%" (
    set "DateTime=%DateTime: AM=%"
) else if not "%DateTime: PM=%" == "%DateTime%" (
    set "DateTime=%DateTime: PM=%"
    set "Add12Hours=1"
)

rem Get year, month, day, hour, minute and second from first parameter.
for /F "tokens=1-6 delims=,-./: " %%A in ("%DateTime%") do (
    rem For English US date MM/DD/YYYY or M/D/YYYY
    set "Day=%%B" & set "Month=%%A" & set "Year=%%C"
    rem For German date DD.MM.YYYY or English UK date DD/MM/YYYY
    rem set "Day=%%A" & set "Month=%%B" & set "Year=%%C"
    set "Hour=%%D" & set "Minute=%%E" & set "Second=%%F"
)

rem Remove leading zeros from the date/time values or calculation could be wrong.
if "%Month:~0,1%"  == "0" if not "%Month:~1%"  == "" set "Month=%Month:~1%"
if "%Day:~0,1%"    == "0" if not "%Day:~1%"    == "" set "Day=%Day:~1%"
if "%Hour:~0,1%"   == "0" if not "%Hour:~1%"   == "" set "Hour=%Hour:~1%"
if "%Minute:~0,1%" == "0" if not "%Minute:~1%" == "" set "Minute=%Minute:~1%"
if "%Second:~0,1%" == "0" if not "%Second:~1%" == "" set "Second=%Second:~1%"

rem Add 12 hours for time range 01:00:00 PM to 11:59:59 PM,
rem but keep the hour as is for 12:00:00 PM to 12:59:59 PM.
if %Add12Hours% == 1 if %Hour% LSS 12 set /A Hour+=12

set "DateTime="
set "Add12Hours="

rem Must use two arrays as more than 31 tokens are not supported
rem by command line interpreter cmd.exe respectively command FOR.
set /A "Index1=Year-1979"
set /A "Index2=Index1-30"

if %Index1% LEQ 30 (
    rem Get number of days to year for the years 1980 to 2009.
    for /F "tokens=%Index1% delims= " %%Y in ("3652 4018 4383 4748 5113 5479 5844 6209 6574 6940 7305 7670 8035 8401 8766 9131 9496 9862 10227 10592 10957 11323 11688 12053 12418 12784 13149 13514 13879 14245") do set "Days=%%Y"
    for /F "tokens=%Index1% delims= " %%L in ("Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N") do set "LeapYear=%%L"
) else (
    rem Get number of days to year for the years 2010 to 2038.
    for /F "tokens=%Index2% delims= " %%Y in ("14610 14975 15340 15706 16071 16436 16801 17167 17532 17897 18262 18628 18993 19358 19723 20089 20454 20819 21184 21550 21915 22280 22645 23011 23376 23741 24106 24472 24837") do set "Days=%%Y"
    for /F "tokens=%Index2% delims= " %%L in ("N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N") do set "LeapYear=%%L"
)

rem Add the days to month in year.
if "%LeapYear%" == "N" (
    for /F "tokens=%Month% delims= " %%M in ("0 31 59 90 120 151 181 212 243 273 304 334") do set /A "Days+=%%M"
) else (
    for /F "tokens=%Month% delims= " %%M in ("0 31 60 91 121 152 182 213 244 274 305 335") do set /A "Days+=%%M"
)

rem Add the complete days in month of year.
set /A "Days+=Day-1"

rem Calculate the seconds which is easy now.
set /A "Seconds=Days*86400+Hour*3600+Minute*60+Second"

rem Exit this subroutine.
goto :EOF

rojo添加了有关如何使用wmic(Windows Management Instrumentation命令行实用程序)忽略日期和时间格式设置、文件系统和Windows版本的信息。
使用wmic具有日期/时间字符串固定格式的优点,因此批处理代码可以在所有Windows计算机上工作,而无需适应本地日期/时间格式。
此外,Windows版本对使用wmic没有影响,因为内部使用了GetFileTime函数(很可能)。
文件系统只在评估本地时间/文件时间到UTC的分钟偏移量时才变得重要。命令wmic在FAT驱动器上返回+***作为分钟偏移量到UTC,而分钟偏移量对于NTFS驱动器上文件的最后修改文件时间是正确的。
NTFS和FAT32驱动器上的相同文件示例:

NTFS:  20071011192622.000000+120
FAT32: 20071011192622.000000+***

+120是中欧时间+60分钟和夏令时+60分钟的总和,即中欧夏令时+120分钟。
因此,下面的批处理代码不计算UTC的分钟偏移量,这在比较文件的最后修改文件时间与当前本地时间时是不需要的。
此外,在wmic命令行中指定文件名时必须始终使用完整路径。仅指定当前目录下的文件名或使用相对路径的文件名是不起作用的。并且每个反斜杠必须在文件名中使用多个反斜杠进行转义。
使用wmic的主要缺点是速度。根据Process Monitor日志,上面的批处理代码在我的Windows XP电脑上需要大约50毫秒才能完成(多次运行的平均值)。下面的批处理代码调用wmic两次,在Windows XP上完成同一任务需要大约1500毫秒(也是一个平均值)。由于这个原因,在大量文件上使用wmic绝对不是一个好主意,至少在Windows XP上,如果可以避免,因为本地日期/时间格式是已知的。
wmic解决方案的另一个缺点是在Windows 11 build 22572和更新版本的Windows上缺少wmic.exe的可用性,这些版本是全新安装的,而不是从以前的Windows版本升级的。请阅读Microsoft文档页面Deprecated features for Windows client,其中包含以下信息:

Windows Management Instrumentation命令行(WMIC)工具

WMIC工具在Windows 10版本21 H1和Windows Server的21 H1常规可用性渠道版本中已弃用。此工具已被Windows PowerShell for WMI取代。此弃用仅适用于command-line management tool。WMI本身不受影响。
有可以安装wmic.exe通过设置〉应用程序〉可选功能上市WMIC在Windows 10/11上安装在目录中缺少%SystemRoot%\System32\wbem.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
if exist %SystemRoot%\System32\wbem\wmic.exe goto GetCurrent

echo ERROR: The WMI command-line utility wmic.exe is not installed.
echo(
echo There should not be used anymore batch files with WMIC on this computer.
echo(
echo There could be installed WMIC via Settings ^> Apps ^> Optional features.
echo(
pause
exit /B

:GetCurrent
rem Get seconds since 1970-01-01 for current date and time.
for /F "tokens=2 delims==." %%T in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do call :GetSeconds %%T

rem Subtract seconds for 4 hours (4 * 3600 seconds) from seconds value.
set /A "CompareTime=Seconds-4*3600"

rem Define batch file itself as the file to compare time by default.
set "FileToCompareTime=%~f0"

rem If batch file is started with a parameter and the parameter
rem specifies an existing file (or directory), compare with last
rem modification date of this file.
if not "%~1" == "" if exist "%~f1" set "FileToCompareTime=%~f1"

rem Get seconds value for the specified file.
set "DataFile=%FileToCompareTime:\=\\%"
for /F "usebackq tokens=2 delims==." %%T in (`%SystemRoot%\System32\wbem\wmic.exe DATAFILE where "name='%DataFile%'" GET LastModified /VALUE`) do call :GetSeconds %%T

rem Compare the two seconds values.
if %Seconds% LSS %CompareTime% (
    echo File %FileToCompareTime% was last modified for more than 4 hours.
) else (
    echo File %FileToCompareTime% was last modified within the last 4 hours.
)
endlocal
goto :EOF

rem Date/time format used by the command line utility of Windows
rem Management Instrumentation is always YYYYMMDDHHmmss with a
rem dot and a 6 digit microsecond value and plus/minus 3 digit
rem time offset to UTC in minutes independent on region and
rem language settings. Microsecond is always 000000 for a file
rem time. The minutes offset including time zone offset and
rem current daylight saving time offset is returned for a file
rem time only for a file on an NTFS drive. Minutes offset is
rem +*** for a file on a FAT drive (at least on Windows XP).

:GetSeconds
rem Get year, month, day, hour, minute and second from first parameter.
set "DateTime=%~1"
set "Year=%DateTime:~0,4%"
set "Month=%DateTime:~4,2%"
set "Day=%DateTime:~6,2%"
set "Hour=%DateTime:~8,2%"
set "Minute=%DateTime:~10,2%"
set "Second=%DateTime:~12,2%"
rem echo Date/time is: %Year%-%Month%-%Day% %Hour%:%Minute%:%Second%

rem Remove leading zeros from the date/time values or calculation could be wrong.
if %Month:~0,1%  == 0 set "Month=%Month:~1%"
if %Day:~0,1%    == 0 set "Day=%Day:~1%"
if %Hour:~0,1%   == 0 set "Hour=%Hour:~1%"
if %Minute:~0,1% == 0 set "Minute=%Minute:~1%"
if %Second:~0,1% == 0 set "Second=%Second:~1%"

rem Must use two arrays as more than 31 tokens are not supported
rem by command line interpreter cmd.exe respectively command FOR.
set /A "Index1=Year-1979"
set /A "Index2=Index1-30"

if %Index1% LEQ 30 (
    rem Get number of days to year for the years 1980 to 2009.
    for /F "tokens=%Index1% delims= " %%Y in ("3652 4018 4383 4748 5113 5479 5844 6209 6574 6940 7305 7670 8035 8401 8766 9131 9496 9862 10227 10592 10957 11323 11688 12053 12418 12784 13149 13514 13879 14245") do set "Days=%%Y"
    for /F "tokens=%Index1% delims= " %%L in ("Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N") do set "LeapYear=%%L"
) else (
    rem Get number of days to year for the years 2010 to 2038.
    for /F "tokens=%Index2% delims= " %%Y in ("14610 14975 15340 15706 16071 16436 16801 17167 17532 17897 18262 18628 18993 19358 19723 20089 20454 20819 21184 21550 21915 22280 22645 23011 23376 23741 24106 24472 24837") do set "Days=%%Y"
    for /F "tokens=%Index2% delims= " %%L in ("N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N") do set "LeapYear=%%L"
)

rem Add the days to month in year.
if "%LeapYear%" == "N" (
    for /F "tokens=%Month% delims= " %%M in ("0 31 59 90 120 151 181 212 243 273 304 334") do set /A "Days+=%%M"
) else (
    for /F "tokens=%Month% delims= " %%M in ("0 31 60 91 121 152 182 213 244 274 305 335") do set /A "Days+=%%M"
)

rem Add the complete days in month of year.
set /A "Days+=Day-1"

rem Calculate the seconds which is easy now.
set /A "Seconds=Days*86400+Hour*3600+Minute*60+Second"

rem Exit this subroutine.
goto :EOF
llmtgqce

llmtgqce2#

或者,你可以使用C#创建一个简单的程序,例如:

// compile using c:\Windows\Microsoft.NET\Framework\v3.5\csc FileByAge.cs
using System;
using System.IO;

public static class Program
{
  public static void Main(string[] args)
  {
    double age = double.Parse(args[0]);
    string fileName;
    while ((fileName = Console.ReadLine()) != null)
    {
      if(age >= (DateTime.Now - new FileInfo(fileName).CreationTime).TotalHours)
      {
        continue;
      }
      Console.WriteLine(fileName);    
    }
  }
}

然后,该程序可以用于查找批处理文件中所有超过2小时的文件,如下所示:

for /F "delims=" %f in ('dir /B *.txt^|FileByAge.exe 2') do echo %f

自编译改进版

正如@Paul所提到的,人们甚至可以构建一个hacky混合批处理/ c#文件,它将编译和运行自己:

@echo off
setlocal
set CsFile=%TEMP%\%~n0.cs
set ExeFile=%TEMP%\%~n0.exe
pushd %SystemRoot%\Microsoft.NET\Framework
for /F %%f in ('dir /B /O:-N v*') do cd %%f & goto :BreakLoop;  
:BreakLoop
(echo /* & type "%~f0") > "%CsFile%"
csc.exe /nologo /out:"%ExeFile%" "%CsFile%" 
del "%CsFile%"
popd
more | "%ExeFile%" %*
del "%ExeFile%"
endlocal
goto:eof
*/

using System;
using System.IO;

public static class Program
{
    public static void Main(string[] args)
    {
        var olderThen = args[0].Contains("older");
        var targetAge = double.Parse(args[1]);
        string fileName;
        while ((fileName = Console.ReadLine()) != null)
        {
            if (string.Empty == fileName)
            {
                continue;
            }
            var fileAge = (DateTime.Now - new FileInfo(fileName).CreationTime).TotalHours;
            if (olderThen ? targetAge > fileAge : targetAge < fileAge)
            {
                continue;
            }
            Console.WriteLine(fileName);
        }
    }
}

可以这样使用:

for /F "delims=" %f in ('dir /B *.txt^|FileByAge.bat older 2') do echo %f
p5fdfcr1

p5fdfcr13#

这个问题的解决方案将在很大程度上取决于预期的时间范围,因为AFAIK如果没有外部程序,你不会在批处理文件中获得时间作为一个整数。这意味着你必须在脚本中解析时间值,以及所需的解析量和计算量。这取决于你需要它工作的范围。这里有一个简单的解决方案。它假设文件不能超过24小时。

set "filename=myfile.txt"
rem extract current date and time
for /f "tokens=1-5 delims=.:, " %%a in ("%date% %time%") do (
  set day=%%a&set mon=%%b&set yr=%%c&set hr=%%d&set min=%%e
)
rem extract file date and time
for /f "tokens=1-5 delims=.:, " %%a in ('"dir %filename%|find "%filename%""') do (
  set fday=%%a&set fmon=%%b&set fyr=%%c&set fhr=%%d&set fmin=%%e
)
rem calculate age of file (in minutes)
set /a "age=((hr*60+min)-(fhr*60+fmin)+(24*60))%%(24*60)"
set /a "max=4*60"
if %age% geq %max% echo.file is older than 4 hours
bvjveswy

bvjveswy4#

纯批处理在日期计算方面很糟糕。如果你的脚本在午夜后不久运行会发生什么?如果时间检查跨越了夏令时的变化会发生什么?
我建议调用Windows Script Host并借用一种更好地处理日期/时间数学的语言。这里有一个混合的批处理/ JScript脚本,可以很容易地计算文件或目录的年龄。将其保存为.bat扩展名并给予一下。

@if (@CodeSection==@Batch) @then

@echo off
setlocal

set "file=C:\Path\To\File.ext"

for /f %%I in ('cscript /nologo /e:JScript "%~f0" "%file%"') do (
    rem // value contained in %%I is in minutes.

    if %%I gtr 240 (

        echo %file% is over 4 hours old.

        rem // do stuff here to take whatever actions you wish
        rem // if the file is over 4 hours old.
    )
)

goto :EOF

@end // end batch portion / begin JScript hybrid chimera

var fso = new ActiveXObject("scripting.filesystemobject"),
    arg = WSH.Arguments(0),
    file = fso.FileExists(arg) ? fso.GetFile(arg) : fso.GetFolder(arg);

// Use either DateCreated, DateLastAccessed, or DateLastModified.
// See http://msdn.microsoft.com/en-us/library/1ft05taf%28v=vs.84%29.aspx
// for more info.

var age = new Date() - file.DateLastModified;
WSH.Echo(Math.floor(age / 1000 / 60));

有关批处理/ JScript混合脚本的更多信息,请访问see this GitHub page。上面使用的样式类似于该页面上的Hybrid.bat。这个答案是based on another。值得一提的是,PowerShell还擅长处理日期数学;但是WSH执行得更快。

相关问题