From b22eca120fe6c8fe43aae049de94496bed1d9dd0 Mon Sep 17 00:00:00 2001 From: Don Armstrong Date: Mon, 29 May 2017 17:10:06 -0500 Subject: [PATCH] switch to perl version of run when changed --- run_when_changed | 142 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 113 insertions(+), 29 deletions(-) diff --git a/run_when_changed b/run_when_changed index 8ad3a27..0fbcf57 100755 --- a/run_when_changed +++ b/run_when_changed @@ -1,29 +1,113 @@ -#!/bin/sh - -WATCH_FULL_FILE="$1" -shift 1; - -if ! [ -e "$WATCH_FULL_FILE" ]; then - echo "No such file or directory $WATCH_FULL_FILE"; -fi; - -WATCH_DIR=`dirname "$WATCH_FULL_FILE"` -WATCH_FILE=`basename "$WATCH_FULL_FILE"` - -if which inotifywait > /dev/null 2>&1; then - inotifywait -m -e close_write,moved_to,create $WATCH_DIR | - while read -r directory events filename; do - if [ "x$filename" = "x$WATCH_FILE" ]; then - ( exec "$@" ); - fi; - done; -else - CURR_TIME=$(stat -c '%Y' "$WATCH_FULL_FILE"); - while sleep 1; do - NEW_TIME=$(stat -c '%Y' "$WATCH_FULL_FILE"); - if [ "x$NEW_TIME" != "x$CURR_TIME" ]; then - CURR_TIME=$NEW_TIME - ( exec "$@" ); - fi; - done; -fi; +#!/usr/bin/perl +# run_when_changed runs a command when a file (or files) is changed +# and is released under the terms of the GNU GPL version 3, or any +# later version, at your option. See the file README and COPYING for +# more information. +# Copyright 2017 by Don Armstrong . + + +use warnings; +use strict; + +use Pod::Usage; +use Linux::Inotify2; +use File::Basename; + +=head1 NAME + +run_when_changed - runs a command when a file (or files) is changed + +=head1 SYNOPSIS + + run_when_changed [file1] [command ...] + run_when_changed [file1] [filen] -- [command ...] + + Options: + --debug, -d debugging level (Default 0) + --help, -h display this help + --man, -m display manual + +If only a single file is to be watched, the first argument is the +file, and all remaining arguments are the command. If multiple files +are to be watched, all arguments until the first -- argument are files +to be watched. If you wish to use a command containing -- and watch a +single file, -- must be the second argument. + +=head1 EXAMPLES + + run_when_changed file.Rnw make file.pdf; + + run_when_changed file.Rnw file_2.Rnw -- make file.pdf; + +=cut + +my @USAGE_ERRORS; +if (@ARGV < 2) { + push @USAGE_ERRORS,"You must provide at least a file and a command to run"; +} + +pod2usage(join("\n",@USAGE_ERRORS)) if @USAGE_ERRORS; + +my ($watch_files,$command) = + identify_files_and_command(@ARGV); + +my $watching = set_up_file_watching($watch_files); + +watch_and_run_command($watching,$command); + + +sub identify_files_and_command { + my (@arguments) = @_; + + my $file_end=0; + my $command_start=1; + for my $i (0..$#arguments) { + if ($arguments[$i] eq '--') { + $file_end=$i-1; + $command_start=$i+1; + last; + } + } + my @files = @arguments[0..$file_end]; + my @command = @arguments[$command_start..$#arguments]; + return (\@files,\@command); +} + +sub set_up_file_watching { + my ($files) = @_; + my $watching = {files => $files}; + my $inotify = new Linux::Inotify2 + or die "Unable to create new inotify object: $!"; + for my $file (@{$files}) { + my $watched_dir = dirname($file); + next if exists $watching->{dirs_watched}{$watched_dir}; + $inotify->watch($watched_dir, + IN_CLOSE_WRITE|IN_MOVED_TO|IN_CREATE, + ) or die "Unable to watch file $file: $!"; + $watching->{dirs_watched}{$watched_dir} = 1; + } + $watching->{inotify} = $inotify; + return $watching; +} + +sub watch_and_run_command { + my ($watching,$command) = @_; + + while () { + my @events = $watching->{inotify}->read; + for my $event (@events) { + my $run_command = 0; + for my $file (@{$watching->{files}}) { + if ($event->w->name eq dirname($file) and + $event->name eq basename($file) + ) { + $run_command = 1; + } + } + if ($run_command) { + system(@{$command}); + } + } + } +} +__END__ -- 2.39.2