batch-file - write - windows script



在批處理文件中查找文件是否超過4小時 (3)

在數學日期,純批次是可怕的。 如果你的腳本在午夜之後不久運行會怎樣? 如果時間檢查跨越夏令時改變怎麼辦?

我建議調用Windows腳本宿主,並從一個更好地處理日期/時間數學的語言借用。 這是一個混合批處理/ 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混合腳本的更多信息, 請參閱此GitHub頁面 。 上面使用的樣式類似於該頁面上的Hybrid.bat。 這個答案是基於另一個 。 對於什麼是值得的,PowerShell也擅長處理日期數學; 但是WSH執行起來要快得多。

https://code.i-harness.com

很簡單,如何找出文件夾中的特定文件是否超過X小時? 文件名和位置將始終相同。

謝謝


在比較文件時間與批處理代碼之後,必須考慮到:

  1. 日期和時間格式設置
  2. 存儲介質的文件系統
  3. 時區,夏令時
  4. 自動調整夏令時
  5. Windows的版本

1.日期和時間格式設置

有環境變量DATETIME (取決於Windows區域和語言設置訪問 (而不是開始批處理執行)返回當前日期和時間格式。 國家定義日期和時間格式。

甚至有可能在那裡定義例如用DD.MM.YYYY (具有前導零的日期和月份)和具有HH:mm:ss時間格式的德語短日期格式(具有小時,分鐘和秒的前導零的24小時格式)。 但是,這並不意味著DATETIME的價值確實在使用這種格式。 例如H:mm:ss,ms當選擇德國國家時, TIME的格式為H:mm:ss,ms ,即使HH:mm:ss被定義,也就是說在小時和分鐘之間沒有前導零,而在逗號之後還要加上毫秒。 日期字符串也可以有或沒有工作日,具體取決於地區設置。

並有一個模式%~t來獲取目錄或文件的最後修改日期和時間。 在命令提示符窗口中運行call /?for /? 並閱讀為這兩個命令顯示的所有幫助頁面的細節。 %~t 〜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文件系統上, 文件時間使用兩種存儲格式:

  • 在使用NTFS的存儲介質上,使用的NTFS精度時間是從1601年1月1日12:00協調世界時(UTC)起經過的100納秒間隔的64位數。

  • 在使用FAT,FAT16,FAT32或exFAT的存儲介質上,文件時間使用DOS日期時間格式存儲,具有兩個16位值,分辨率為2秒,並使用當前本地時間。

這意味著一個奇怪的秒是不可能的FAT文件上的文件,但在NTFS媒體上。 將上次修改時間為奇數秒的NTFS驅動器上的文件複製到FAT32驅動器,導致在上次修改時間中具有1秒差異的相同文件。

NTFS媒體上的文件時間以UTC格式存儲。 那麼%~t或命令DIR返回的內容以及在Windows資源管理器中顯示的內容依賴於什麼

  • 當前時區,
  • 夏令時為此時區,
  • 如果當前時間在夏令時間內,
  • 並且如果當前啟用夏令時自動調整。

更改這4個參數中的任何一個都會立即改變NTFS存儲介質上文件和目錄的所有顯示文件時間。

使用本地時間創建,修改或上次訪問時,FAT介質上的文件時間或多或少是不變的,但是請閱讀詳細信息。

3.時區,夏令時

由於NTFS介質上的文件時間是以UTC格式存儲的,但是在本地時間顯示,所以此時區的當前時區和夏令時對於文件時間比較非常重要,特別是對於NTFS和FAT32驅動器上的文件。

但是對於FAT介質上的文件,根據Windows版本的不同,時區和夏令時設置也很重要。 請閱讀下面的細節。

4.自動調整夏令時

夏令時自動調整設置為默認啟用時變得重要,當前設置的時區定義夏令時調整。

例如夏令時在歐洲目前是活躍的。 在Windows的時鐘設置中禁用此選項會導致在NTFS介質上顯示的文件時間立即發生-1小時更改,並且FAT介質上的許多文件也取決於Windows的版本。

在比較NTFS和FAT介質上的文件時間時,必須考慮由夏令時調整引起的1小時時差。

5. Windows版本

所有Windows NT版本都支持NTFS。 所有支持NTFS的Windows版本上的文件和目錄的文件時間行為是相同的。

但是如何解釋FAT介質上的文件時間取決於Windows的版本。 之前的Windows Vista在FAT介質上的文件時間在時區更改,夏令時設置和夏令時自動調整上都是獨立的。 在Windows 2000和Windows XP上,文件的上次修改日期/時間是不變的。

但是,在Windows Vista和更高版本上,在FAT介質上顯示的文件時間取決於夏令時和自動調整夏令時。 時區不直接關係。 當前使用的另一個時區選擇不會像在NTFS介質上的文件那樣更改FAT介質上文件的顯示文件時間。 但是,如果當前時間在活動時區的夏時制時間段內,並且啟用了夏令時自動調整,並且該文件或目錄的本地時間在夏時制時間範圍內,則Windows Vista將添加+1小時然後。 在標準時間內最後在FAT驅動器上修改的文件在一年中是不變的; 只有在DST期間最後修改的文件根據當前時間顯示帶或不帶+1小時,並啟用自動DST調整。

例如,在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.比較文件時間和當前時間

對於比較文件上次修改時間和當前時間的任務,僅考慮日期和時間格式設置就足夠了。

下面的批處理代碼使用我在批處理文件的答案中詳細解釋的代碼來刪除N天以前的文件 。 在使用下面的代碼之前應該閱讀這個解釋。

根據本地Windows PC上的文件%~tI返回的變量DATETIME以及日期/時間字符串的日期/時間格式,可能需要對代碼進行小的修改。 批處理代碼的註釋行中給出了適當的提示。

@echo off
setlocal EnableExtensions

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 "%DateTime: AM=%" NEQ "%DateTime%" (
    set "DateTime=%DateTime: AM=%"
) else if "%DateTime: PM=%" NEQ "%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%"  EQU "0" ( if "%Month:~1%"  NEQ "" set "Month=%Month:~1%"   )
if "%Day:~0,1%"    EQU "0" ( if "%Day:~1%"    NEQ "" set "Day=%Day:~1%"       )
if "%Hour:~0,1%"   EQU "0" ( if "%Hour:~1%"   NEQ "" set "Hour=%Hour:~1%"     )
if "%Minute:~0,1%" EQU "0" ( if "%Minute:~1%" NEQ "" set "Minute=%Minute:~1%" )
if "%Second:~0,1%" EQU "0" ( if "%Second:~1%" NEQ "" 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 2 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

通過使用wmic -Windows Management Instrumentation命令行實用程序, rojo添加了有關日期和時間格式設置,文件系統和Windows版本可以被忽略的信息。

使用wmic具有固定格式的日期/時間字符串的優點,因此批處理代碼可以在所有Windows計算機上工作,而不適應本地日期/時間格式。

此外,由於內部使用GetFileTime函數(很有可能),使用wmic的Windows版本並不重要。

文件系統在評估本地時間/文件時間到UTC的分鐘偏移時變得非常重要。 命令wmic返回FAT驅動器只是+***的分鐘偏移到UTC,而分鐘偏移量正確的NTFS驅動器上的文件的最後修改文件時間。

NTFS和FAT32驅動器上的相同文件的示例:

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

+120是CET(中歐時間)+60分鐘的總和,積極的夏令時+60分鐘,或換句話說,CEST(中歐夏令時)+120分鐘。

因此,下面的批處理代碼不會計算與當前本地時間比較文件上次修改文件時間時不需要的UTC偏移量。

此外,文件名必須在wmic命令行中始終使用完整路徑指定。 只需指定當前目錄中的文件的文件名或具有相對路徑的文件名不起作用。 並且每個反斜杠必須用帶有路徑的文件名中的一個反斜杠進行轉義。

使用wmic的主要缺點是速度。 上面的批處理代碼需要在我的計算機上根據Process Monitor日誌(幾次運行的平均值)大約50 ms完成。 以下調用wmic兩次的批處理代碼需要約1500毫秒的完成相同的任務(也是一個平均值)。 因此,在大量文件上使用wmic是絕對不是一個好主意,如果可以避免,因為本地日期/時間格式是已知的。

@echo off
setlocal EnableExtensions

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 echo 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%"  EQU "0" ( if "%Month:~1%"  NEQ "" set "Month=%Month:~1%"   )
if "%Day:~0,1%"    EQU "0" ( if "%Day:~1%"    NEQ "" set "Day=%Day:~1%"       )
if "%Hour:~0,1%"   EQU "0" ( if "%Hour:~1%"   NEQ "" set "Hour=%Hour:~1%"     )
if "%Minute:~0,1%" EQU "0" ( if "%Minute:~1%" NEQ "" set "Minute=%Minute:~1%" )
if "%Second:~0,1%" EQU "0" ( if "%Second:~1%" NEQ "" set "Second=%Second:~1%" )

rem Must use 2 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

解決方案將取決於預期的時間範圍,因為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




batch-processing