So I downloaded a bunch of documents in chronological order, but I have to copy and edit them, which would change the date modified. I would like to prefix them with a number (001_, 002_...) in the order I downloaded them in.
-
1And what have you tried so far? That's part of the research for each question. – Eduardo Trápani May 12 '21 at 18:22
-
I found a solution on this site that appends the date modified at the end of the filename, but that one doesn't do much for me. There's only 667 files so I could do it by hand, but this is not the first nor is it the last time I need to do something like this. – user471414 May 12 '21 at 18:41
-
The idea is to come here with a question. If you want to add information to your original question, you edit it (instead of posting an answer). By the way "that doesn't do much for me" doesn't do much either for those trying to help. Please add to you question the problem you have with the solution you found. An error message, for example. – Eduardo Trápani May 12 '21 at 19:36
3 Answers
With zsh
:
autoload zmv
n=0; zmv -n '*(#qOm)' '${(l[3][0])$((++n))}_$f'
(remove the -n
(for dry-run) when happy).
That's:
zmv
: autoloadable function to rename files using zsh extended globs and substitution operators*
: all non-hidden files in the current directory(#q...)
: the verbose form of introducing glob qualifiersOm
: order in reverse by age. So newest last.${(l[3][0])$((++n))}
: left-pad the expansion of$((++n))
on width 3 with 0s._$f
and append_
and the original filename

- 544,893
-
Doesn't do anything for me. I'm using Linux Mint, if that helps. This zsh is installed but "autoload zmv" gives "command not found". – user471414 May 12 '21 at 18:48
-
@user471414, you need to run those commands from within the zsh shell. The default shell on Linux Mint is bash, a shell with a similar syntax but much more limited. You can start zsh from bash, by just entering zsh at the prompt, or make zsh your default interactive shell instead of bash with
chsh
. – Stéphane Chazelas May 12 '21 at 19:02
Found the solution here.
ls -1tr | rename -v 's/.*/our $i;if(!$i){$i=1;} sprintf("%04d.pdf", $i++)/e'

- 21
find . -type f -printf '%C@ %p\0' |
sort -z -n |
awk 'BEGIN{RS=ORS="\0"};{print $2}' |
rename -0 -n 'our $i;
s:(^.*/)([^/]*):sprintf("%s%04d_%s",$1,++$i,$2):e'
This uses GNU find
to print NUL separated filenames (%p
, so the full path) prefixed by their modification timestamp (%C@
) and a space character in unix seconds-since-the-epoch numerical format.
If required, you can use any of find
's other options here to refine the file search (e.g. -name "*.pdf"
, -maxdepth 2
, etc)
find
's output is then piped into GNU sort
using the -z -n
options to sort the NUL-separated input numerically (by the timestamps), and then into awk to strip the timestamp and the space, and then finally piped into a perl rename script.
The perl rename script uses the -0
option to read NUL separated records from STDIN, and then:
uses
sprintf()
with '%s%04d_%s' (%04d_
is zero-padded, 4 digits wide and an underscore), using$i
as a counter to replace the filename portion of the name.$1
is the path portion of the full filename,$++i
is an auto-incrementing counter variable, and$2
is the base filename.$i
is declared withour
to make it a global variable, so it keeps its value for each iteration of the loop (otherwise it gets reset to zero every time).
The -n
option turns this into a dry-run operation, so that it only shows what would be renamed. Once you're sure it's going to do what you want, remove -n
to quietly rename the files with no output, or replace it with -v
for verbose output.
Unlike using ls -1tr
this will NOT break if any of the filenames contain newlines. It will work with any valid filename.
FYI, to append a nicely-formatted date to each filename, see my rename
script at Renaming a bunch of files with date modified timestamp at the end of the filename?. This is easily modified to prefix the filename with date & time. It runs stat
on each file to get the timestamp, so the filenames wouldn't need to be pre-sorted.
e.g.
rename -n 'BEGIN {use Date::Format};
die $! unless -f $_;
next if (m/^\d{8}-$/);
my $ts=(stat($_))[9];
my $dt=time2str("%Y%m%d-%H%M%S",$ts);
$_ = "$dt-$_"' *.pdf

- 78,579