2

I have an application which write to a file. Before I run the application, I would like to rotate the file.

In other words; rename the existing files so that file.n becomes file.n+1, (and file.1 is renamed to file.2), without overwriting existing files.

I could write a script to do this, but I was wondering if there is a simpler way?

Alexander
  • 9,850
  • 2
    logrotate is the standard tool for doing this, I believe. – Josh Jolly Mar 26 '14 at 12:23
  • 1
    @slm logrotate may be the solution here, too, but it is not the same question. The problem here is different and thus not answered for the other question. Thus I suggest against closing this one (at least due to the question you pointed at). – Hauke Laging Mar 26 '14 at 13:31
  • 1
    @slm logrotate is pretty awkward for doing this to files arbitrarily -- you'd have to sit and write a config, etc. – goldilocks Mar 26 '14 at 13:45
  • @HaukeLaging - Goldilocks - there are 2 options in that dup. logrotate which might be awkward but also rotatelogs. That tool, though included with Aapche, can be used via command line as a one liner. – slm Mar 26 '14 at 14:19
  • rotatelogs (that isn't a typo) http://httpd.apache.org/docs/2.4/programs/rotatelogs.html would seem to be a good option here. It's the accepted A in the dup I highlighted. – slm Mar 26 '14 at 14:21
  • @HaukeLaging - I'll retract but it still feels like a dup to me. – slm Mar 26 '14 at 14:26
  • 1
    @slm I missed that point (about rotatelogs -- I was also unaware of it. Of course installing apache for one simple command seems a bit much, but that's besides the point). This is the more general question, it might be good to combine them -- I'll move this to chat. – goldilocks Mar 26 '14 at 15:18
  • @HaukeLaging ^^ – goldilocks Mar 26 '14 at 15:23

1 Answers1

5

I can write a script to do this

Here's the perl version if you want to save some time:

#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);

# Rotate files (file -> file.1, file.1 -> file.2, etc).

if ($#ARGV < 0 || !-e -w $ARGV[0] || index($ARGV[0], '/') != -1) {
        print "Existing file basename required\n";
        exit 1;
}

my $name = $ARGV[0];

opendir my $dh, './';
my @files = ();
my $last = 0;
while (readdir $dh) {
        next if !($_ =~ m/^$name\.(\d+)$/);
        $last = $1 if $1 > $last;
}
close $dh;

for (my $i = $last; $i > 0; $i--) {
        rename "$name.$i", "$name.".($i + 1);
}

rename $name, "$name.1";

You can only use this on files in the current working directory. e.g.:

rotate whatever.file

If there's a path of any sort attached, it will throw an error. It would not be hard to modify to allow for paths, however.

Also, if there's a whatever.file.0 it will be ignored (the files it produces are numbered starting from 1).

goldilocks
  • 87,661
  • 30
  • 204
  • 262
  • 1
    That is nice, but needs a little fix, to work as intended. Replace for (my $i = $last; $last > 0; $last--) { by for (my $i = $last; $i > 0; $i--) { and it works. – LeSpocky Mar 19 '19 at 16:24