0

I have copied files from an old backup where all the files have been appended with a timestamp of the backup so a file that was previously called "background.jpg" has now been named "background (2022_12_15 14_28_40 UTC).jpg" in the backup. I would like to remove all of these timestamps from where they occur in these filenames, restoring the filenames back to what they were originally.

I found a very similar question here: Using CMD on Windows to remove a specific substring from a directory of files

However, I had difficulty with both of the answers here. The shorter, one-line script, I tried running like this:

cmd /v:on /c "for /R %f in (* ^(2022_12_15 14_28_40 UTC^).*) do @set "nn=%~nf" & echo ren "%~ff" "!nn:~0,-26!%~xf""

(In the above code, including "echo" previews the rename (ren) operation without actually doing it, while omitting "echo" actually executes the series of renames.) I had to escape the parentheses in the substring to get it to run without an .*) was unexpected at this time error. But it didn't work correctly -- rather than focus only on files with the substring specified, it applied this operation to all files, which did fix all the files with the substring, but it also caused some problems because it wiped out the filename of files without this substring and which were 26 characters or shorter. Also, there were these erroneous rename commands added for the top level folder and each subfolder within:

ren "C:\Users\[folder or subfolder]\(2022_12_15" ""
ren "C:\Users\[folder or subfolder]\14_28_40" ""

There are no such files in any of the folders or subfolders with just that name. Looking at this while writing this question, it looks like CMD is interpreting this fileset as "*" AND "(2022_12_15" AND "14_28_40" AND "UTC).*", which explains that fileset it decided on, but I couldn't figure out how to get spaces to not register as separating different tokens that way but rather part of the filename specification itself (escaping with ^ didn't work). Some help?

I also tried Mofi's answer, but with some adjustments made to work with my particular substring:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "delims=" %%I in ('dir "%~dp0* (2022_12_15 14_28_40 UTC).*" /A-D /B /S 2^>nul') do (
    set "FullFileName=%%I"
    set "FileNameOnly=%%~nI"
    set "FileExtension=%%~xI"
    setlocal EnableDelayedExpansion
    if /I "!FileNameOnly:~-26!" == " (2022_12_15 14_28_40 UTC)" ren "!FullFileName!" "!FileNameOnly:~0,-26!!FileExtension!"
    endlocal
)
endlocal

(My file looks like above except with a lot of comments inserted as notes to explain to myself what was going on.)

But using the above, I run into some issues. When I run it just like above, I get this error:

. was unexpected at this time.

Which I think happens on the "for" line, since echos above print out but echos below do not, although I think I may not understand how echos work in Windows batch files because: when I change nothing except add an echo following the "for ... do (" line:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "delims=" %%I in ('dir "%~dp0* (2022_12_15 14_28_40 UTC).*" /A-D /B /S 2^>nul') do (
    echo I1: %%I
    set "FullFileName=%%I"
    set "FileNameOnly=%%~nI"
    set "FileExtension=%%~xI"
    setlocal EnableDelayedExpansion
    if /I "!FileNameOnly:~-26!" == " (2022_12_15 14_28_40 UTC)" ren "!FullFileName!" "!FileNameOnly:~0,-26!!FileExtension!"
    endlocal
)
endlocal

I get this output and the program continues:

I1: C:\Users\[filepath redacted]\background (2022_12_15 14_28_40 UTC).jpg
The system cannot find the drive specified.
The system cannot find the drive specified.

I don't understand why adding an echo here changes the behavior of the program.

When the program continues, it doesn't make any changes to files. I've done some testing and it seems that the IF check fails. However, the logic of the if and rename operations seems sound because if I hardcode the three variables -- FullFileName, FileNameOnly, and FileExtension -- to be the expected values for a specific file:

    set "FullFileName=C:\Users\[filepath redacted]\background (2022_12_15 14_28_40 UTC).jpg"
    set "FileNameOnly=background (2022_12_15 14_28_40 UTC)"
    set "FileExtension=.jpg"

Then this script renames the file as expected. If I echo the rename operation like this, this is what I get:

    echo ren "!FullFileName!" "!FileNameOnly:~0,-26!!FileExtension!"

Prints:
    ren "C:\Users\[filepath redacted]\background (2022_12_15 14_28_40 UTC).jpg" "background.jpg"

Also, there are many files in this folder and its subfolders, but the file being singled out by this script (after I add the above echo) is correctly the only file with this substring in the filename.

But using the set commands above does not trigger the IF in the same way these manual sets do. When I try to print out %%I later in the script, I never again get the value that was printed out the first time I echo'd:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "delims=" %%I in ('dir "%~dp0* (2022_12_15 14_28_40 UTC).*" /A-D /B /S') do (
    echo I1: %%I   <-- prints "I1: C:\Users\[filepath redacted]\background (2022_12_15 14_28_40 UTC).jpg"
    set "FullFileName=%%I"
    set "FileNameOnly=background (2022_12_15 14_28_40 UTC)"
    set "FileExtension=.jpg"
    echo I2: %%I   <-- prints "I2: %I"
    setlocal EnableDelayedExpansion
    if /I "!FileNameOnly:~-26!" == " (2022_12_15 14_28_40 UTC)" (
        :: The code goes into the IF here because of the hardcoded variables above.
        echo I3: %%I   <-- prints "I3: %I"
        echo ren "!FullFileName!" "!FileNameOnly:~0,-26!!FileExtension!"   <-- prints "ren "%I" "background.jpg"
    )
    endlocal
)
endlocal

I'm just so stuck and I need help. I don't know how to actually set the local variables properly using the for loop variable %%I. I don't understand why this script won't work, and I don't understand why inserting an echo temporarily fixed it (but the variable-setting still won't work). Does this have something to do with the parentheses in my substring? With the spaces? I would just appreciate some help on how to fix up either the one-line code so that it targets the correct fileset, or fix up the more complicated batch file so that the for and set lines work properly.

  • I suppose you should change 2-nd line in batch to this: `setlocal EnableDelayedExpansion`. Then remove line `setlocal EnableDelayedExpansion` from `FOR`-cycle. And I see no reason for condition check in `FOR`-cycle: remove `IF`-operator – Daemon-5 Aug 08 '23 at 01:59
  • I tested with a file `F:\Temp\[filepath redacted]\Development & Test(!) 100% (2022_12_15 14_28_40 UTC).jpg` the first two batch files posted by you adapted from my code stored in `F:\Temp`. The batch files worked as expected renaming the file to `F:\Temp\[filepath redacted]\Development & Test(!) 100%.jpg`. `echo I1: %%I` and `echo I2: %%I` printed twice the fully qualified file name as posted here. The third variant with `echo I3: %%I` cannot work with that file name because of enabled delayed variable expansion on second processing of the command line after `%%I` replaced by the file name. – Mofi Aug 08 '23 at 07:44
  • I suppose the code really used in the batch file was not exactly the code posted in the answer. The error message `. was unexpected at this time.` is not explainable for me, except there was used a code not exactly as posted because of a missing `"` or a `)` somewhere in batch file or during execution in a file name and the entire argument string is not enclosed in `"`. All `"` in the batch file code are extremely important on processing file names with `)` in file name to get the closing round bracket in file name interpreted as literal character and not as end of a command block. – Mofi Aug 08 '23 at 07:44
  • @Daemon-5 Read the explanation of the batch file code in [my answer](https://stackoverflow.com/a/62643815/3074564) to get knowledge why delayed variable expansion is disabled above the __FOR__ loop and enabled just for the __IF__ condition and the __REN__ command execution. That is necessary to process correct also file names containing anywhere in fully qualified file name (drive + path + name + extension) one or more `!`. The reason for the __IF__ condition is also explained in my answer and should not be omitted to really rename all files correct. – Mofi Aug 08 '23 at 07:49
  • Please note that a line beginning with `::` (invalid label) inside a command block beginning with `(` and ending with `)` results in __undefined behavior__ on execution of the batch file. There must be used the __command REM__ which does not mean that the string after command name __REM__ and the space character is completely ignored by `cmd` on processing a batch file. __REM__ is a __command__ and the rest of the command line is parsed like all other command lines described by [How does the Windows Command Interpreter (CMD.EXE) parse scripts?](https://stackoverflow.com/questions/4094699/) – Mofi Aug 08 '23 at 07:56
  • So, if you have had a `)` in a __REM__ command line inside body of the __FOR__ loop __during execution__ of the batch file, this closing round bracket closed the body of the __FOR__ loop. Invalid labels beginning with `::` (after zero or more leading spaces/tabs) are a __NO GO__ in a command block and are used best never in a batch file, except for a readers information at top of the batch file never shown on execution of the batch file even with command echo mode turned on. – Mofi Aug 08 '23 at 08:02
  • `for %%a in (one two*) ...` lists files named `one` and files matching `two*` (same with `dir`). Enclose the whole file path in quotes: `for %%a in ("F:\ile path\with spaces\*"` or `... in ('dir "F:\ile path\file name*"') do ...` (with the correct file mask of course). – Stephan Aug 08 '23 at 08:32

1 Answers1

-1

I cut the Gordian knot a bit with this issue and went this route:

  1. Install Cygwin.
  2. Write a .sh script based heavily on this: https://stackoverflow.com/a/27283349/22353749
  3. (Need to make sure the line breaks are Unix-style in order for the script to run properly.)
  4. Note that one issue with the script linked above is the lack of quotes around the sed substitution specification, which causes problems if the substring you're removing has spaces. Something like this worked for me:
find . -name "* (2022_12_15 14_28_40 UTC).*" -print0 | while read -d $'\0' file
do
    mv "file" "$(echo $file | sed 's/ (2022_12_15 14_28_40 UTC)//')"
done
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 14 '23 at 07:20