rename lets you assign directly to
$_, which is ideal for this problem.
Although the code arguments we pass to the Perl
rename utility often perform matching and substitution (with
s/), and it is totally acceptable to solve this problem that way, there's really no need for that in cases like this one where you're not actually examining or reusing the text of the old filenames.
rename lets you write just about any Perl code, and it doesn't need to be embedded inside
/e. I suggest using a command like this:
rename -n 'our $i; $_ = sprintf "AS%02d.txt", ++$i' *
Or, if you feel like shaving off a few characters:
rename -n '$_ = sprintf "AS%02d.txt", ++our$i' *
(It could be made even shorter, but not without sacrificing clarity.)
Both those commands do the same thing. Each shows what renaming operations would be undertaken. Once you try one out and you're happy with the results, run it again without the
-n option. As written, this would produce the filenames
AS11.txt, and so on. You can modify them as needed, then only remove
-n when you like what you see.
If you want to pad to a greater width than 2, replace the
2 with a bigger number. For example, if for some reason you wanted filenames like
AS00000000000001.txt, you would replace
%014d. If you don't want padding at all--even though that would make your files appear in non-numeric order when you list them later!--then you can replace
%02d with just
How does this work? Your shell expands
* into a list of files before it even runs the
rename command. The list is sorted lexicographically (i.e., alphabetically) according to your current locale settings.
rename receives the filenames as command-line arguments and processes them in the order they are listed. The expression
++$i evaluates to one greater than the current value of the variable
$i, and also sets the variable
$i to that newly incremented value. This value is passed to
sprintf, which formats it and places it inside other text. This text is assigned directly to the special variable
$_, which holds the filename. Since the
rename processes files in the order they are passed as arguments, they are numbered accordingly.
You may have noticed the similarity to Oli's answer!
Both declare and increment a counter variable, and both use
sprintf in essentially the same way to embed the value of that counter, padded on the left with zeros, in the other text of the new filenames. (The information in either answer about how to pad with zeros--and change the padding width--even applies equally to both.) The reason they are so similar is that the method in that older answer is really doing just this1, but with extra steps that are useful for many problems but not actually necessary for this one.
For comparison, here's what the second command in that answer looks like if modified to use the style I've used here (and to pad to a width of 2, as I've done here, instead of 3):
rename -n 's/.+/our $i; sprintf "AS%02d.txt", ++$i/e' *
The part between
/e in that command is now the same as the expression I assign to
$_ (i.e., the code on the right side of the
= operator) in the first command in this answer.
s/ operator attempts to match text (filenames that your shell expanded from
* and passed as command-line arguments when it ran
rename) with a regular expression, and performs substitution.
/ is being used as a delimiter to separate the different parts of the
s expression. The first part,
.+, is the regular expression; it matches any1 character (
.), and does so one or more times (
+). Since filenames are never empty--they're always at least one character long--this is one way to match any1 filename.
- Then it produces text from each match--that is, from each whole filename--consisting of
our $i; sprintf "AS%02d.txt", ++$i. This produces the new filename. Typically, one uses
s/ to produce text that is either (a) replacing text that matched just part of the filename, or (b) based on the the matched text in some way (for example, it might use a special variable like
$& that expands to the match). In this case, though, the matched text is not being used at all.
- Normally the substituted text
our $i; sprintf "AS%02d.txt", ++$i would be used as a filename, not run as code. But the
/e flag at the end causes that text to be treated as Perl source code and evaluated, and uses the value produced by doing so (in this case, the return value of Perl's built-in
sprintf function) as the new filename.
Although I think the method I've shown here is a clearer and simpler approach for this problem than any method that uses
/e, it's good to be aware of that technique, because it is well suited to a number of other file renaming problems where all or part of the original filenames is examined and/or retained. I recommend this blog post by Oli (who also wrote that answer), which includes some such examples.
1 Technically, there is a difference in the behavior of the
$_ = commands and the
/e commands. In the regular expression
.+, it's not strictly true that
. matches any character, because it does not match a newline. You very probably shouldn't use filenames with newlines in them, but you can, and occasionally a file gets named that way by accident. If you feel like it, contrast the output of
rename -n 'our $i; $_ = sprintf "AS%02d.txt", ++$i' $'foo\nbar' to that of
rename -n 's/.+/our $i; sprintf "AS%02d.txt", ++$i/e' $'foo\nbar'. To make
. match a newline, use the
s flag, which goes at the end (with
e). That is, write
/se at the end instead of