问题
I have following files in bulk to modify xxx.001
, xxx.002
etc. by adding .txt
at the end of the file name. In order to do this I am using following command:
rename XXXX*.* *?.txt
However it inserts .txt
twice instead of once.
回答1:
A solution is renaming the files matching a wildcard pattern using a FOR loop.
for %%I in (xxx*) do ren "%%I" "%%I.txt"
This FOR loop in a batch file searches in current directory for non-hidden files of which file name matches the wildcard pattern xxx*
. Each file name matching the wildcard pattern is assigned to loop variable I
without file path and without double quotes even on containing a space or one of these characters &()[]{}^=;!'+,`~
requiring enclosing the file name in double quotes. The command REN renames the file by appending .txt
.
The problem with this simple command line is that the list of file names in current directory changes while command FOR iterates over the list of file names matching the wildcard pattern. This command line could work nevertheless on an NTFS formatted storage media because of NTFS stores directory entries in a local specific alphabetic sort and the sort of the file names in current directory does not change because of just .txt
is appended to some file names. But on FAT16, FAT32 or exFAT formatted storage medias this simple command line could fail to work as expected. Some files could be renamed more than once and others could be skipped because of directory entries on FAT drives are not stored sorted like on NTFS drives.
The solution is getting first the list of files to rename into memory and then rename one after the other. Then the list of file names does not change anymore during the loop iterations. This can be achieved with following command line:
for /F "eol=| delims=" %%I in ('dir "xxx*" /A-D /B 2^>nul') do ren "%%I" "%%I.txt"
FOR with option /F
runs in this case in background one more command process started with %ComSpec% /c
and the command line appended specified between '
. This means with Windows installed to C:\Windows
that FOR executes in background:
C:\Windows\System32\cmd.exe /c dir "xxx*" /A-D /B 2>nul
Read 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 in a separate command process started in background.
DIR executed by cmd.exe
in background searches
- in current directory
- for file names of non-hidden and hidden files because of option
/A-D
(attribute not directory) - matching the wildcard pattern
xxx*
, i.e. any file name starting withxxx
, - and outputs in bare format just file name + file extension without file path never enclosed in
"
because of option/B
.
The error message output by DIR if not finding any directory entry matching these criteria to handle STDERR is redirected to device NUL to suppress it.
The output by DIR (list of file names) to handle STDOUT of background command process is captured by FOR and processed line by line after started cmd.exe
terminated itself. This is the important difference to first command line. The list of file names is now completely in memory and FOR iterates over this list of file names not changing anymore during the loop iterations.
FOR with option /F
ignores empty lines which do not exist in captured list of file names.
FOR with option /F
would split up each line into substrings using horizontal tab and normal space as string delimiters. This line splitting behavior is not wanted here in case of a file name contains one or more spaces. For that reason delims=
is used at end of options argument string to define an empty list of delimiters which disables the line splitting behavior completely.
FOR with option /F
would ignore the entire line if the first substring after line splitting (not done in this case) has a semicolon as first character. A file name can start with ;
and for that reason eol=|
is used to define vertical bar as end of line character which no file can contain in its file name. So no file name is ever ignored by FOR. In this case with DIR outputting just file names starting with xxx
the default eol=;
could be used as well and therefore eol=|
would not be necessary.
So FOR assigns one file name after the other output by DIR without path to loop variable I
and runs the command REN to rename this file by appending .txt
.
But there is one problem left if the current directory contains already files starting with xxx
and having already file extension .txt
because of these files would be also output by DIR and therefore processed by FOR resulting in appending one more .txt
.
There are at least two solutions to avoid renaming files having already file extension .txt
:
The output of command DIR is filtered with FINDSTR which outputs just all file names NOT ending with the case-insensitive and literally interpreted string
.txt
by using:for /F "eol=| delims=" %%I in ('dir "xxx*" /A-D /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /E /L /I /V /C:".txt"') do ren "%%I" "%%I.txt"
An additional IF condition is used comparing case-insensitive the file extension of current file name with the string
.txt
to determine if REN should be executed on current file by using:for /F "eol=| delims=" %%I in ('dir "xxx*" /A-D /B 2^>nul') do if /I not "%%~xI" == ".txt" ren "%%I" "%%I.txt"
Open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully for additional information about the used commands and their options.
dir /?
findstr /?
for /?
if /?
ren /?
来源:https://stackoverflow.com/questions/58171888/adding-2nd-file-extension-to-file-with-certain-prefix