Search code examples
batch-filecmddirectory

List time/date created, time/date modified, and file name for all files in subdirectories


I have a batch file to retrieve files from a shared workspace.

@echo off
setlocal
for /f "tokens=*" %%f in ('dir "D:\Share" /a-d-h /b /s') do (
  for /f "tokens=1,2,3" %%g in ('dir "%%f" /tc ^| findstr /C:"%%~nxf"') do (
    echo %%h,%%i,%%g,"%%~nf","%%~xf
    )
  )>>D:\test.csv
endlocal

I would like to modify the first four output columns to:

  • Time Created HH:MM
  • Date Created YYYY-MM.DD
  • Last Modified HH:MM Last
  • Modified YYYY-MM.DD

Apologies in advance as new to this, also open to any redesign if there is a better process.


Solution

  • @ECHO OFF
    SETLOCAL
    FOR /f  "delims=" %%b IN ('dir /b /s /a-d-h "xx.csv" ') DO (
     ECHO %%b
     FOR /f "tokens=1-3" %%g IN ('dir /tc "%%b"^|findstr /c:"%%~nxb"') DO (
      CALL :report %%g %%h %%i %%~tb "%%~nb" "%%~xb"
    rem  CALL :report 04/30/2016 08:46 AM 08/05/2016 01:17 PM "%%~nb" "%%~xb"
     )
    )
    
    GOTO :EOF
    
    :report
    rem parameters are cdate ctime campm mdate mtime mampm name extension (cdate=createdate...)
    rem datestamp format is 08/05/2016 01:17 PM for Aug. 5th 2016 @ 1:17pm
    rem required output format
    rem Time Created HH:MM Date Created YYYY-MM.DD Last Modified HH:MM Last Modified YYYY-MM.DD name extension
    
    CALL :YYYY-MM.DD cdate %1
    CALL :24hr ctime %2 %3
    CALL :YYYY-MM.DD mdate %4
    CALL :24hr mtime %5 %6
    
    ECHO %ctime% %cdate% %mtime% %mdate% %~7 %~8
    GOTO :eof
    
    :: convert from mm/dd/yyyy to YYYY-MM.dd
    
    :YYYY-MM.DD 
    SET "raw=%2"
    SET "%1=%raw:~-4%-%raw:~0,2%.%raw:~3,2%"
    GOTO :eof
    
    :: convert from hh:mm ampm to HH:MM
    
    :24hr
    SET "raw=%2"
    rem Exx 00:17 -> 12:17; 01:17 -> 01:17; 12:17 -> 12:17; 13:17 -> 01:17
    IF "%raw:~02%"=="12" (SET /a raw=100) ELSE (SET "raw=1%raw:~0,2%")
    rem Exx 00:17 -> 100  ; 01:17 -> 101  ; 12:17 -> 100  ; 13:17 -> 101
    IF /i "%3" == "PM" SET /a raw+=12
    rem Exx 00:17 -> 100  ; 01:17 -> 101  ; 12:17 -> 112  ; 13:17 -> 113
    SET "raw=%raw%%2"
    rem Exx 00:17 -> 10012:17; 01:17 -> 10101:17; 12:17 -> 11212:17; 13:17 -> 11312:17
    SET "%1=%raw:~1,2%%raw:~-3%"
    GOTO :eof
    

    For testing purposes, I targeted a single file and removed the appending to the report file.

    The :report subroutine takes 8 parameters - two sets of [date time ampm] and the file's name and extension. The name and extension can potentially contain spaces, so should be quoted.

    it converts the dates supplied to the required format using subroutines :YYYY-MM.DD for the date and :24hr for the time end echoes the results.

    :YYYY-MM.DD is simple. It assigns the date provided as the second parameter to a temporary variable, then uses substringing to deliver the required string to the variable specified as the first parameter.

    Substringing is covered by set /? from the prompt or many articles on SO. It's important to note that the first character in a string is character 0, not 1.

    You should not use temp or tmp for a user-variable as BOTH of these variables contain the name of a directory used to store temporary files. In the early days, some people used tmp and others temp for this purpose, so setting both suits both groups and maintains compatibility.

    :24hr is a little more complex. Again %1 is used to specify the return variable name. Its schtick is to convert 12 as an hour number to 00 if the time is AM and leave it as 12 for PM. Other hours, add 12 for PM.

    The gotcha here is that if left to its own devices, batch will suppress leading zeroes and regards a leading 0 as specifying the value as octal.

    So - if the first 2 characters of the time are 12, start with 100, otherwise, start with 1 prepended to the hour (consequently, 09:xx would become 109- valid decimal instead of 09 - invalid octal).

    Then add 12 if the time is PM.

    Append the original time field to the result, and pick the 2nd and third character with the last 3 for return to %1.

    Since my date/time format is DD/MM/YYYY HH:MM, it will produce nonsense when processed by the :report routine, so I tested using a set of constants as I believe they would be presented on a MM/DD/YYYY hh:mm ampm system (now remmed-out)

    Further thoughts

    I got wmic to work on my Win11 system.

    SETLOCAL ENABLEDELAYEDEXPANSION 
    SET "filepath=c:\106x"
    SET "Filename=withtrailsp.txt"
    
    set "EscapedFilePath=%FilePath:\=\\%"
    
    FOR /f "usebackqtokens=1*delims=." %%b IN (
     `%SystemRoot%\System32\wbem\wmic.exe DATAFILE where "name='%EscapedFilePath%\\%Filename%'" GET "CreationDate"^,"LastAccessed"^,"LastModified" /VALUE`
    ) DO IF "%%c" neq "" SET %%b 
    SET la
    SET cr
    

    Since it seems that wmic is required in order to get seconds for Created then it'll take the same amount of time to get all of the time fields using wmic with the advantage that the format is ISO (at least it is for me)

    Note that this snippet uses delayedexpansion and that the \ in filepaths needs to be doubled. Note also that the commas in the fields list also need to be escaped. The filename is just one I knew had different create/modified/accessed dates.