]> git.donarmstrong.com Git - bin.git/blob - run_when_changed
add reset usb bus command
[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 If only a single file is to be watched, the first argument is the
26 file, and all remaining arguments are the command. If multiple files
27 are to be watched, all arguments until the first -- argument are files
28 to be watched. If you wish to use a command containing -- and watch a
29 single file, -- must be the second argument.
30
31 =head1 EXAMPLES
32
33      run_when_changed file.Rnw make file.pdf;
34
35      run_when_changed file.Rnw file_2.Rnw -- make file.pdf;
36
37 =cut
38
39 my @USAGE_ERRORS;
40 if (@ARGV < 2) {
41     push @USAGE_ERRORS,"You must provide at least a file and a command to run";
42 }
43
44 pod2usage(join("\n",@USAGE_ERRORS)) if @USAGE_ERRORS;
45
46 my ($watch_files,$command) =
47     identify_files_and_command(@ARGV);
48
49 my $watching = set_up_file_watching($watch_files);
50
51 watch_and_run_command($watching,$command);
52
53
54 sub identify_files_and_command {
55     my (@arguments) = @_;
56
57     my $file_end=0;
58     my $command_start=1;
59     for my $i (0..$#arguments) {
60         if ($arguments[$i] eq '--') {
61             $file_end=$i-1;
62             $command_start=$i+1;
63             last;
64         }
65     }
66     my @files = @arguments[0..$file_end];
67     my @command = @arguments[$command_start..$#arguments];
68     return (\@files,\@command);
69 }
70
71 sub set_up_file_watching {
72     my ($files) = @_;
73     my $watching = {files => $files};
74     my $inotify = new Linux::Inotify2
75         or die "Unable to create new inotify object: $!";
76     for my $file (@{$files}) {
77         my $watched_dir = dirname($file);
78         next if exists $watching->{dirs_watched}{$watched_dir};
79         $inotify->watch($watched_dir,
80                         IN_CLOSE_WRITE|IN_MOVED_TO|IN_CREATE,
81                        ) or die "Unable to watch file $file: $!";
82         $watching->{dirs_watched}{$watched_dir} = 1;
83     }
84     $watching->{inotify} = $inotify;
85     return $watching;
86 }
87
88 sub watch_and_run_command {
89     my ($watching,$command) = @_;
90
91     while () {
92         my @events = $watching->{inotify}->read;
93         for my $event (@events) {
94             my $run_command = 0;
95             for my $file (@{$watching->{files}}) {
96                 if ($event->w->name eq dirname($file) and
97                     $event->name eq basename($file)
98                    ) {
99                     $run_command = 1;
100                 }
101             }
102             if ($run_command) {
103                 system(@{$command});
104             }
105         }
106     }
107 }
108 __END__