2

I frequently use a free online lossless file compressor to save space on my disk and make transferring and pushing repos easier. My main problem with the compressor is it appends "-min" to the end of every filename. For various reasons, I want to replace the original files by overwriting them; instead of deleting the old files and keeping the new files (with the "new" names).

For my PDF directory, I tried this: FOR /R %f IN (*-min.pdf) DO REN "%f" *.pdf

And it seems to correctly find all the corresponding files, but their names remain the same. What am I doing incorrectly? Is there a single command that would be file-format-agnostic, so I wouldn't have to sub out the file extension for txt's, png's, etc?

I just need it to remove the -min from the end of each filename (before the file extension).

  • The FOR variable %f is assigned the whole file name, not the "*" matched part; so you are renaming the file with the same name. You have to do some string processing, wich is a rather tricky thing in Windows batch, and I don't think it can be done in a one line command. If %f is a file name the expression %~nf returns the name without extension; %s:0,-4% returns all but the last 4 characters of a string s, and so on. Search "windows batch string processing". – Giovanni Zezza Jun 29 '20 at 17:16

2 Answers2

2

FOR /R %f IN (*-min.pdf) DO REN "%f" *.pdf

For a one liner (at the cmd prompt) remove echo from the following.

cmd /v:on /c "for /R %f in (*-min.pdf) do @set "nx=%~nxf" & echo ren "%~ff" "!nx:-min.pdf=.pdf!""

The above can be easily changed to perform other string manipulations, but it is not technically foolproof, for example it will rename a file named "a-min.pdf.b-min.pdf-c-min.pdf" to "a.pdf.b.pdf-c.pdf.". If that is a concern in this case, then use the following, which is essentially the same as in @Mofi's answer.

cmd /v:on /c "for /R %f in (*-min.pdf) do @set "nn=%~nf" & echo ren "%~ff" "!nn:~0,-4!%~xf""
dxiv
  • 16,984
  • 2
  • 27
  • 49
  • 1
    It isn't essentially the same as @Mofi's answer. `for /R` passes to the `do` as command soon as it gets its first result and continues to do so as it iterates. This means any changes to a file name in the `do` command could be cycled again into the running `for /R` command. Whilst in this case, as you're removing the `-min` substring, this won't affect anything, it's an important distinction to make; because Mofi's parenthesized command method is all processed before being passed to the `do` portion. As this is tagged `cmd` and not `batch-file` however yours would be more correct in this case. – Compo Jun 30 '20 at 00:48
  • @Compo Good points. I used `for /R` to keep it close to OP's attempt and, indeed, the loop is restricted to `*-min.pdf` so that renamed files don't match the wildcard. One other difference is that the loop written this way works fine with filenames with international characters outside the active codepage. – dxiv Jun 30 '20 at 01:53
1

The task can be done with a batch file with following command lines:

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

The twice used negative value -4 must match with the length of the string to remove from the end of the file name left to the file extension. The string -min has a length of four characters which is the reason for using here twice -4.

The command DIR executed by a separate command process started by FOR in background with %ComSpec% /c and the command line between ' appended as additional arguments outputs also file names like Test-File!-min.x.pdf with full path. For that reason the IF condition makes sure to rename only files of which file name really ends case-insensitive with the string -min like Test-File!-MIN.pdf.

Read the Microsoft documentation 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.

The code works even for files of which fully qualified file name (drive + path + name + extension) contains one or more exclamation marks because of delayed variable expansion is enabled only for the command line which finally renames a file.

The file renaming task can be done faster with permanently enabled delayed variable expansion as long as no file contains anywhere one or more ! in its fully qualified file name.

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

This code does not work for renaming Test-File!-MIN.pdf to Test-File!.pdf because of ! in file name.

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

  • call /? ... explains %~dp0 ... drive and path of argument 0 which is the full path of the batch file which always ends with a backslash. It is concatenated with the wildcard pattern *-min.* for that reason without an additional backslash. %~dp0 can be removed to run DIR on current directory and all its subdirectories instead of batch file directory and all its subdirectories.
  • dir /?
  • echo /?
  • endlocal /?
  • if /?
  • ren /?
  • set /?
  • setlocal /?

See also this answer for details about the commands SETLOCAL and ENDLOCAL.

Mofi
  • 46,139
  • 17
  • 80
  • 143