Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

onchange - a script to run a command when a file changes

by samtregar (Abbot)
on Nov 20, 2003 at 20:19 UTC ( [id://308680]=sourcecode: print w/replies, xml ) Need Help??
Category: Utility Scripts
Author/Contact Info Sam Tregar <sam@tregar.com>
Description: I like to write articles in POD and preview the results of running pod2html in my web browser. However, this requires me to run a command in a shell everytime I make an edit. Even if it's just 'make' I still can't be bothered.

So I wrote this script to run a command everytime a file changes. It requires Time::HiRes and Getopt::Long. Read the POD for more information.

UPDATE: I added code to do a recursive walk when onchange is passed a directory. This enables onchange to watch a whole directory tree. It won't notice new files added though... (Feb 24th 2005)

#!/usr/bin/perl -w
use strict;
use Time::HiRes qw(sleep);
use Getopt::Long;
use Pod::Usage;

=head1 NAME

onchange - a little program to run a command when a file changes

=head1 SYNOPSIS

Run C<make> command whenever a source file is updated:

  $ onchange make *.c *.h &

Run C<pod2html> on a .pod file whenever it is updated:

  $ onchange 'pod2html foo.pod > foo.html' foo.pod &

Run C<make> whenever any file in the current directory, or any
directory below, is updated (ignoring files starting with .):

  $ onchange make .

=head1 OPTIONS

A couple options are available for your use:

=over

=item --verbose

See the command everytime it runs.

=item --sleep 0.1

Set the granularity of the check for file changes in seconds.
Defaults to 0.5.

=back

=cut

my $verbose = 0;
my $sleep = 0.5;
pod2usage(2) unless
  GetOptions('verbose'  => \$verbose,
             'sleep=f'  => \$sleep);

pod2usage("Too few arguements.") unless @ARGV >= 2;
my ($command, @input_files) = @ARGV;

# expand directories in @input_files list with a recursive walk and
# fill in @mtimes with mtimes for files
my (@files, @mtimes);
while (my $f = shift @input_files) {
    if (-f $f) {
        push(@files, $f);
        push(@mtimes, (stat(_))[9]);
    } elsif (-d _) {
        opendir(DIR, $f);
        push @input_files, 
          map { "$f/$_" }
            grep { $_ !~ /^\./ } readdir(DIR);
    } else {
        die "File '$f' does not exist or is not a file/directory.";
    }
}

while(1) {
    for (my $x = 0; $x < @files; $x++) {
        my $mtime = (stat($files[$x]))[9];
        next unless $mtime;
        if ($mtime != $mtimes[$x]) {
            print STDERR "Running '$command'\n" if $verbose;
            system($command);
        }
        $mtimes[$x] = $mtime;
    }
    sleep($sleep);
}
Replies are listed 'Best First'.
Re: onchange - a script to run a command when a file changes
by Zaxo (Archbishop) on Nov 20, 2003 at 21:25 UTC

    This code appears to run the same command on the same file for each updated file in @ARGV[1..$#ARGV]. The list form of system in a perl-style loop over file basenames would work better.

    I'd be inclined to do this with make, either called from perl or in a frequent cron job.

    After Compline,
    Zaxo

      This code appears to run the same command on the same file for each updated file in @ARGV[1..$#ARGV].

      Well, it doesn't run a command "on the same file" exactly. It just runs a command. I agree it would be more useful if it could somehow supply the filename of the changed file to the command. I can't seem to find it right now, but I had something that did just that. I used File::Signature to watch the file (which was probably overkill.)

      I'd be inclined to do this with make, either called from perl or in a frequent cron job.

      By default, he is checking for changes every half a second. Cron wouldn't make a good substitute for that.

      -sauoq
      "My two cents aren't worth a dime.";
      
      I considered adding an option to supply the file that changed, but I decided it wasn't that useful to me. Obviously it would be an easy change.

      As far as using cron, it takes too much work to setup and take down a cronjob. I wanted something I could setup very quickly and kill as soon as I was done with a simple Ctrl-C.

      -sam

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://308680]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (3)
As of 2024-04-25 09:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found