1

I need to batch append a date modified timestamp to a filenames in Windows 10. I am almost there.

The answer here was extremely helpful in getting me nearly to my objective.

The batch script below creates appends the file's date modified, but omits the seconds YYYYMMDDHHMM instead of YYYYMMDDHHMMSS (what I need).

My computer's date format is YYYY-MM-DD, and time format hh:mm:ss.

How can I achieve this?

@echo off
FOR /R "J:\PHP Member Reports\Test" %%Z IN (*.TRN) DO @( 
    FOR /F "Tokens=1-6 delims=:-\/. " %%A IN ("%%~tZ") DO @(
        ren "%%~dpnxZ" "%%~A%%~B%%~C%%~D%%~E%%~F_%%~nZ%%~xZ"
    )
)
tsouchlarakis
  • 1,499
  • 3
  • 23
  • 44
  • The `F` in what command? You haven't provided an MCVE. Take the [tour], read [Ask] and [MCVE]. There are two answers in the link you supplied and neither of them was accepted as an answer. There is much better coverage of this topic here: https://stackoverflow.com/questions/203090/how-do-i-get-current-datetime-on-the-windows-command-line-in-a-suitable-format/203116#203116 – jwdonahue Jan 28 '18 at 01:40
  • Possible duplicate of [How do I get current datetime on the Windows command line, in a suitable format for using in a filename?](https://stackoverflow.com/questions/203090/how-do-i-get-current-datetime-on-the-windows-command-line-in-a-suitable-format) – jwdonahue Jan 28 '18 at 01:41
  • @jwdonahue - the `%F` in the inner `for` loop; read the question. – SomethingDark Jan 28 '18 at 01:41
  • @SomethingDark, the question didn't read `%F`, it read `F` – jwdonahue Jan 28 '18 at 01:43
  • Thank you for your responses. I apologize if my question was confusing. I just need to be able to access seconds. I changed my Date/Time locale to YYYY-MM-DD HH-MM-SS. I don't know much about Windows CMD but I was able to figure out that my `%~A` is YYYY, `%~B` is MM, and so forth. `%~E` is my minutes, so the next one should be seconds. But as you pointed out, my inner `for` loop iterates over `F`. Do you know how I can access seconds? Thanks. – tsouchlarakis Jan 28 '18 at 02:07
  • @jwdonahue Thank you for linking that post. I apologize for my inexperience with Windows but I do not know how I can apply that to my situation, without rewriting what I have entirely. I have researched this for hours and haven't found a solution. I have made my question as simple as I could. – tsouchlarakis Jan 28 '18 at 02:10
  • 2
    I notice that you're using the `%A` variable format instead of the `%%A` one. Am I correct in assuming that you need this to work directly on the command line instead of using a batch script for some reason? – SomethingDark Jan 28 '18 at 02:18
  • I was testing on the command line, but putting it in a batch script would be perfectly reasonable. Thanks. – tsouchlarakis Jan 28 '18 at 02:26
  • @SomethingDark I've edited the code to batch script format – tsouchlarakis Jan 28 '18 at 02:51
  • I think `("%%~tZ")` should be `("%~tZ") as the tZ variable is not the one of the loop variables. Your post is not an MCVE. Where's the rest of the script? The work that needs to be done should happen above the for loops. I want to be sure I know all the variables your script is using. – jwdonahue Jan 28 '18 at 03:10
  • @jwdonahue - `%%~tZ` is `%%~Z` with the `t` flag to return the last modified time of the file being processed by the outer `for` loop. Don't write the code off as incomplete just because you don't understand what it does. – SomethingDark Jan 28 '18 at 03:16
  • @SomethingDark, It is incomplete, whether any of us understand it or not. And I never said anything about writing the code off. You're right though, I missed the significance of the `~t`. Still, I think we're missing the part where we have a time stamp to append to the name of the string and I am fairly sure we still have to convert that 12 to 24 hour format. While we're at it, we should probably verify which time format is in use at the time of the running of the script. – jwdonahue Jan 28 '18 at 03:25
  • @tsouchlarakis, please post your entire script. If the above snippet of code is all you've got, that's fine, but I don't want to hand you something you won't be able to integrate on your own. – jwdonahue Jan 28 '18 at 03:29
  • @tsouchlarakis, Are you trying to append the file's own file system date time to the file name? That's how the code reads but it's not how your problem statement reads. – jwdonahue Jan 28 '18 at 03:35
  • @jwdonahue Thanks for your responses, but this is the entire script. It runs perfectly fine as is. I'm not sure what else to include that would make it complete...it already runs just fine! I just need to grab the seconds from the data modified time. The timestamp I am adding is the date modified of the file. I made that more clear in the problem statement. To answer your question about `("%%~tZ")`, @SomethingDark had it correct, and I am using the `%%` because it's a batch script, instead of running on command line, per advice from @SomethingDark – tsouchlarakis Jan 28 '18 at 05:02
  • Well I just posted what I think is a potential answer to your problem. The code converts the AM/PM style time stamp to the 24 hour form if its not already in that form and it avoids the extra cmd.exe instance. It will be noticeably faster if you have hundreds for thousands of files. Mostly the style is just easier to accomplish what I finally realized you were after. – jwdonahue Jan 28 '18 at 05:08
  • We can probably modify the script to get seconds, but we have to acquire the file time stamps by other means. As far as I know the best that the dir command can do is HH:MM. – jwdonahue Jan 28 '18 at 05:18

2 Answers2

2

It is necessary to use wmic – the Windows Management Instrumentation Command-line utility – to get last modification date of a file also with second.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "RenameError=0"
for /F "delims=" %%I in ('dir "J:\PHP Member Reports\Test\*.trn" /A-D-H /B /S 2^>nul') do call :ProcessFile "%%I"
if %RenameError% == 1 echo/ & pause
endlocal
goto :EOF

:ProcessFile
set "FileName=%~1"
set "FileName=%FileName:\=\\%"
set "FileDate="

rem Get last modification date of current file using Windows Management
rem Instrumentation Command-line utility (WMIC) which requires that each
rem backslash is escaped with a backslash.
for /F "usebackq tokens=2 delims==." %%T in (`%SystemRoot%\System32\wbem\wmic.exe DATAFILE where "name='%FileName%'" GET LastModified /VALUE 2^>nul`) do set "FileDate=%%T"
if defined FileDate goto RenameFile

rem The command line above fails if a comma exists anywhere in
rem file name with full path. Use in this case a different syntax.
if not defined FileDate for /F "usebackq tokens=2 delims==." %%T in (`%SystemRoot%\System32\wbem\wmic.exe DATAFILE where (name^="%FileName%"^) GET LastModified /VALUE 2^>nul`) do set "FileDate=%%T"
if defined FileDate goto RenameFile

rem The command line above fails again if there is a comma and additionally
rem also a closing parenthesis in file name with full path. To workaround
rem this issue copy the file with name WmicFile.tmp to the directory for
rem temporary files of which path does not contain usually , and ) at the
rem same time and get last modification date from this file before deleting
rem this temporary file.

copy "%~1" "%TEMP%\WmicFile.tmp" >nul
set "FileName=%TEMP%\WmicFile.tmp"
set "FileName=%FileName:\=\\%"
for /F "usebackq tokens=2 delims==." %%T in (`%SystemRoot%\System32\wbem\wmic.exe DATAFILE where "name='%FileName%'" GET LastModified /VALUE 2^>nul`) do set "FileDate=%%T"
del "%TEMP%\WmicFile.tmp"
if defined FileDate goto RenameFile

if %RenameError% == 1 echo/
echo ERROR: Failed to determine last modification date of file
echo        %1
set "RenameError=1"
goto :EOF

:RenameFile
set "FileName=%~n1"
rem Do nothing if current file name ends already with determined date/time.
if "%FileName:~-15%" == "_%FileDate%" goto :EOF
rem Rename the file with appending after an underscore the
rem last modification date and time in format YYYYMMDDhhmmss.
ren "%~1" "%~n1_%FileDate%%~x1" 2>nul
if not errorlevel 1 goto :EOF

if %RenameError% == 1 echo/
echo ERROR: Failed to rename the file
echo        %1
echo        to "%~n1_%FileDate%%~x1"
set "RenameError=1"
goto :EOF

It is no good idea to use file names retrieved by command FOR itself from a directory or directory tree on renaming, moving, deleting or copying files in directories searched by FOR. This can very easily result in omitting files or processing some files multiple times because of list of entries in a searched directory changes while FOR processes the directory entries. It is strongly recommended to process a list of file names by FOR get completely before starting processing the files, especially on FAT drives (FAT16, FAT32, exFat) on which the file names are returned not sorted at all by the file system.

The command DIR is executed by FOR in a background command process started with cmd.exe /C which outputs to handle STDOUT only all non hidden files in specified directory and its subdirectories matching the wildcard pattern *.trn. This list of file names each with full path is captured by FOR. Read also the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.

Each file name with full path is passed by FOR to subroutine ProcessFile enclosed in double quotes to work also for file names/paths containing a space or one of these characters &()[]{}^=;!'+,`~.

The WMIC syntax requires that each file name is specified with full path and each backslash in file path is escaped with a backslash. See also my answer on Find out if file is older than 4 hours in batch file.

How to escape both comma and closing parenthesis in WHERE clause of WMIC? explains that a full file name containing a comma , or a closing parenthesis ) is problematic. The batch file is written to work around those problems. It can be expected that first executed FOR command line with WMIC is nearly always already successful on determining last modification date of file with second.

The batch file is written to output an error message if last modification date could not be determined for a file.

No rename is executed if a file name ends already with an underscore and the right last modification date/time.

It is verified by the batch file if rename operation was successful with displaying an error message on failed file rename.

Note: The batch file is not written to replace YYYYMMDDhhmmss at end of a file name by correct last modification date/time of this file. This was not required in question.

The batch file halts execution at end with command PAUSE if any error occurred within subroutine ProcessFile. Otherwise the execution of the batch file ends without any user information output to STDOUT (console window).

Please note that WMIC needs quite a long time to finish the operation. So on a large list of files this batch file can take several minutes to complete.

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • call /?
  • copy /?
  • del /?
  • dir /?
  • echo /?
  • endlocal /?
  • for /?
  • goto /?
  • if /?
  • pause /?
  • rem /?
  • ren /?
  • set /?
  • setlocal /?
  • wmic /?
  • wmic datafile /?
  • wmic datafile get /?
  • wmic datafile get "last modified" /?
  • wmic datafile where /?

See also:

Mofi
  • 46,139
  • 17
  • 80
  • 143
1

You could probably do this using RoboCopy which I understand provides a non locale dependent date and time. (Make necessary modifications only on lines 3 and 4)

@Echo Off

Set "filePath=J:\PHP Member Reports\Test"
Set "fileSpec=*.TRN"

Set "RCO=/S /L /XJ /R:0 /TS /FP /NS /NC /NP /NDL /NJH /NJS"
For /F "Tokens=1-6* Delims=/:    " %%A In (
    'RoboCopy "%filePath%" Null "%fileSpec%" %RCO%'
) Do If Not Exist "%%A%%B%%C%%D%%E%%F%%~xG" (
    Echo=Ren "%%G" "%%A%%B%%C%%D%%E%%F%%~xG")
Pause

Where Delims=/:<tab><space> or /:Tab                                          

The script currently only Echoes the Rename commands; remove Echo= from line 10 and Pause from the last line if you are happy with the results.

Also please note that this is to answer your question only. Any problems arising from renaming your files to also match your file specification, or already existing files will need to be tackled separately.

Compo
  • 36,585
  • 5
  • 27
  • 39