0

I have files like

file1.txt
file2.txt
file3.txt
...
file11.txt
file12.txt
...
file100.txt
file101.txt
...

I'd like to rename them to have the same string width like

file001.txt
file002.txt
file003.txt
...
file011.txt
file012.txt
...
file100.txt
file101.txt
...

Is there a simple command to achieve this? I want to avoid manual renames, e.g. using ranger bulkrename.

2
  • 1
    Is the prefix (file) and the suffix (.txt) always the same?
    – aviro
    Nov 14, 2022 at 9:23
  • Yes, always the same, but more general solutions are also welcome
    – rsaavedra
    Nov 14, 2022 at 9:26

3 Answers 3

1

In the manual pages rename(1), you can see an example to do that:

EXAMPLES
       Given the files foo1, ..., foo9, foo10, ..., foo278, the commands

              rename foo foo00 foo?
              rename foo foo0 foo??

       will turn them into foo001, ..., foo009, foo010, ..., foo278

So in your case, the simple solution (in case the maximum number is 3 digit long) would be:

rename file file00 file?.txt
rename file file0 file??.txt

Before run the actual rename command, you can test it by adding --no-act / -n and --verbose / -v flags to see what would be the outcome:

$ rename -nv file file00 file?.txt
`file1.txt' -> `file001.txt'
`file2.txt' -> `file002.txt'
`file3.txt' -> `file003.txt'
$ rename -nv file file0 file??.txt
`file11.txt' -> `file011.txt'
`file12.txt' -> `file012.txt'

A more general solution (available since bash version 3.0):

$ for i in `find ./ -maxdepth 1 -type f -name 'file*.txt'`; \
  do if [[ "$i" =~ file([0-9]*).txt$ ]]; \
    then NUM=$(printf "%03d" "${BASH_REMATCH[1]}"); \
    echo mv --verbose $i $i ./file${NUM}.txt; \
  fi; \
done
mv --verbose ./file1.txt ./file001.txt
mv --verbose ./file2.txt ./file002.txt
mv --verbose ./file3.txt ./file003.txt
mv --verbose ./file11.txt ./file011.txt
mv --verbose ./file12.txt ./file012.txt
mv --verbose ./file100.txt ./file100.txt
mv --verbose ./file101.txt ./file101.txt

Remove the echo when you want to perform the change. It's just a simple example, you can change the prefix and suffix of the files to patterns as well in the if line, and use $BASH_REMATCH to capture those to make more complex renames. For instance:

$ for i in `find ./ -maxdepth 1 -type f`; \
  do if [[ "$i" =~ ^([^0-9]*)([0-9]*).txt$ ]]; \
    then NUM=$(printf "%03d" "${BASH_REMATCH[2]}"); \
    echo mv --verbose $i ${BASH_REMATCH[1]}${NUM}.txt; \
  fi; \
done
mv --verbose ./file1.txt ./file001.txt
mv --verbose ./file2.txt ./file002.txt
mv --verbose ./file3.txt ./file003.txt
mv --verbose ./file11.txt ./file011.txt
mv --verbose ./file12.txt ./file012.txt
mv --verbose ./file100.txt ./file100.txt
mv --verbose ./file101.txt ./file101.txt
mv --verbose ./something1.txt ./something001.txt
mv --verbose ./another02.txt ./another002.txt

Of course in the you can modify the printf command to change the zero padding length from 3 to any other number.

2
  • That exactly answers my question. It would be nice to do it in a single command though, in case there are even larger numbers.
    – rsaavedra
    Nov 14, 2022 at 9:38
  • Note that there are two different versions of a rename command. This answer uses the syntax of the version that is part of the util-linux package. On Ubuntu, rename is by default a symlink to the Perl version named prename. The latter uses a Perl expression to control the renaming.
    – Bodo
    Nov 14, 2022 at 10:14
0

To rename files with all sequences of ASCII decimal digits left-padded to a width of 3 with 0s, with zsh:

autoload -Uz zmv
zmv -n '*' '${f//(#m)<->/${(l[3][0])MATCH}}'

(remove -n for dry-run if happy with the result).

Same with perl-based rename implementations (sometimes called file-rename, prename or perl-rename):

rename -n 's/\d+/sprintf "%03d", $&/ge' ./*[0123456789]*
0

Using the perl rename utility, which is also known as file-rename, perl-rename, or prename. It is not to be confused with the rename utility from util-linux which has completely different and incompatible capabilities and command-line options. Perl rename allows you to use any arbitrarily complex perl code to rename files, but is most often used to do simple sed-like s/search/replace/ operations on filenames.

rename -n 's/^file(\d+)\.txt$/sprintf "file%03i.txt", $1/e' file*.txt

The /e regex modifier tells rename to treat the replacement portion of the s/search/replace/ operator as executable perl code. The sprintf function is used to zero-pad the digits before the .txt extension.

or it can be generalised to work with any filename prefix and extension - e.g.

rename -n 's/\A(.*?)(\d+)(\..*)\z/sprintf "%s%03i%s", $1, $2, $3/esm' *

The /s and /m regex modifiers are used to ensure that the script works with any filename, even those containing newlines. \A is the beginning-of-string anchor and \z is the end-of-string anchor.

Note that the -n option makes it a dry run, so it will only show what it would do without actually renaming any files. Remove the -n, or replace it with -v for verbose output, when you've confirmed that it does what you want.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .