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.

  • 1
    And what have you tried so far? That's part of the research for each question. May 12, 2021 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, 2021 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. May 12, 2021 at 19:36

3 Answers 3


With zsh:

autoload zmv
n=0; zmv -n '*(#qOm)' '${(l[3][0])$((++n))}_$f'

(remove the -n (for dry-run) when happy).


  • 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 qualifiers
  • Om: 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
  • 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, 2021 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. May 12, 2021 at 19:02
find . -type f -printf '%C@ %p\0' |
  sort -z -n |
  awk  'BEGIN{RS=ORS="\0"};{print $2}' | 
  rename -0 -n 'our $i;

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 with our 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.


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

Found the solution here.

ls -1tr | rename -v 's/.*/our $i;if(!$i){$i=1;} sprintf("%04d.pdf", $i++)/e'


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.