]> git.donarmstrong.com Git - bin.git/blob - run_when_changed
add a VERBOSE flag to getmail
[bin.git] / run_when_changed
1 #!/usr/bin/perl
2 # run_when_changed runs a command when a file (or files) is changed
3 # and is released under the terms of the GNU GPL version 3, or any
4 # later version, at your option. See the file README and COPYING for
5 # more information.
6 # Copyright 2017 by Don Armstrong <don@donarmstrong.com>.
7
8
9 use warnings;
10 use strict;
11
12 use Pod::Usage;
13 use Linux::Inotify2;
14 use File::Basename;
15
16 =head1 NAME
17
18 run_when_changed - runs a command when a file (or files) is changed
19
20 =head1 SYNOPSIS
21
22  run_when_changed [file1] [command ...]
23  run_when_changed [file1] [filen] -- [command ...]
24
25  Options:
26    --debug, -d debugging level (Default 0)
27    --help, -h display this help
28    --man, -m display manual
29
30 If only a single file is to be watched, the first argument is the
31 file, and all remaining arguments are the command. If multiple files
32 are to be watched, all arguments until the first -- argument are files
33 to be watched. If you wish to use a command containing -- and watch a
34 single file, -- must be the second argument.
35
36 =head1 EXAMPLES
37
38      run_when_changed file.Rnw make file.pdf;
39
40      run_when_changed file.Rnw file_2.Rnw -- make file.pdf;
41
42 =cut
43
44 my @USAGE_ERRORS;
45 if (@ARGV < 2) {
46     push @USAGE_ERRORS,"You must provide at least a file and a command to run";
47 }
48
49 pod2usage(join("\n",@USAGE_ERRORS)) if @USAGE_ERRORS;
50
51 my ($watch_files,$command) =
52     identify_files_and_command(@ARGV);
53
54 my $watching = set_up_file_watching($watch_files);
55
56 watch_and_run_command($watching,$command);
57
58
59 sub identify_files_and_command {
60     my (@arguments) = @_;
61
62     my $file_end=0;
63     my $command_start=1;
64     for my $i (0..$#arguments) {
65         if ($arguments[$i] eq '--') {
66             $file_end=$i-1;
67             $command_start=$i+1;
68             last;
69         }
70     }
71     my @files = @arguments[0..$file_end];
72     my @command = @arguments[$command_start..$#arguments];
73     return (\@files,\@command);
74 }
75
76 sub set_up_file_watching {
77     my ($files) = @_;
78     my $watching = {files => $files};
79     my $inotify = new Linux::Inotify2
80         or die "Unable to create new inotify object: $!";
81     for my $file (@{$files}) {
82         my $watched_dir = dirname($file);
83         next if exists $watching->{dirs_watched}{$watched_dir};
84         $inotify->watch($watched_dir,
85                         IN_CLOSE_WRITE|IN_MOVED_TO|IN_CREATE,
86                        ) or die "Unable to watch file $file: $!";
87         $watching->{dirs_watched}{$watched_dir} = 1;
88     }
89     $watching->{inotify} = $inotify;
90     return $watching;
91 }
92
93 sub watch_and_run_command {
94     my ($watching,$command) = @_;
95
96     while () {
97         my @events = $watching->{inotify}->read;
98         for my $event (@events) {
99             my $run_command = 0;
100             for my $file (@{$watching->{files}}) {
101                 if ($event->w->name eq dirname($file) and
102                     $event->name eq basename($file)
103                    ) {
104                     $run_command = 1;
105                 }
106             }
107             if ($run_command) {
108                 system(@{$command});
109             }
110         }
111     }
112 }
113 __END__