Skip to content

Instantly share code, notes, and snippets.

@s1037989
Last active November 27, 2020 18:05
Show Gist options
  • Save s1037989/2623f65e85557f1dc5525b57fdf5263c to your computer and use it in GitHub Desktop.
Save s1037989/2623f65e85557f1dc5525b57fdf5263c to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
FILE=`which file 2>/dev/null` || exit 1
finish_writing() {
local file=$1; shift
if [ $still_growing_timeout -gt 0 ]; then
printf "waiting for $file to finish\n" >$stderr
wait_file $file
! file "$file" "$mime_type" && return
! validate "$file" "$test" && return
fi
local _file="$tmpl"
[[ "$_file" =~ %f ]] && _file="${_file/\%f/${file##*/}}"
[[ "$_file" =~ %w ]] && _file="${_file/\%w/${file%/*}/}"
[[ "$_file" =~ %T ]] && _file="${_file/\%T/$(date +%FT%T.%N)}"
[[ "$_file" =~ %s ]] && _file="${_file/\%s/$(stat -c%s $file)}"
echo "$_file"
}
file() {
local file=$1; shift
[ -z "$file" -o ! -f "$file" ] && return 1
local mime_type=$1; shift
[ -z "$mime_type" ] && return 0
[ $($FILE -bp -m ${magic:-/etc/magic} -- mime_type $file) == "$mime_type" ]
}
still_growing() {
local file=$1; shift
[ -z "$file" ] && return 1
test ! -e $file -o ! -s $file && return 0
test $(echo "scale=9; $(date +%s.%N)-$(test -e $file && date -r $file +%s.%N || echo 0) < ${still_growing_timeout:-1}" | bc) -eq 1
}
usage() {
cat <<EOF;
Usage: $0 [OPTIONS]
Wait for a file to finish growing, and optionally detect the file type based on
its contents and pass a test. Each file is tested in a subprocess so files are
reported finished as they finish and not in the order they are created.
Options:
-f tmpl Output template, defaults to "%w%f"
%f Basename
%s Size
%T Current time (%FT%T.%N)
%w Dirname with trailing slash
-h Usage
-M type Mime Type
-m magic Magic file
-q Quiet
-T test Test to pass to validate file
-t sec Timeout to assume no longer growing
Examples:
After 1 second of no growth, assume file is finished writing.
$ inotifywait -m `pwd` | finish_writing
EOF
exit 1
}
validate() {
local file=$1; shift
[ -z "$file" -o ! -f "$file" ] && return 1
local test=$1; shift
[ -z "$test" ] && return 0
eval ${test/\%f/$file} &>/dev/null
}
wait_file() {
local file=$1; shift
[ -z "$file" ] && return 1
while still_growing $file; do sleep $still_growing_pause; done
}
while getopts "f:hM:m:p:qT:t:" o; do
case "$o" in
f) tmpl=$OPTARG;;
h) usage;;
M) mime_type=$OPTARG;;
m) magic=$OPTARG;;
t) still_growing_pause=$OPTARG;;
q) stderr=/dev/null;;
T) test=$OPTARG;;
t) still_growing_timeout=$OPTARG;;
esac
done
shift $(($OPTIND-1))
: ${tmpl:="%f"}
: ${stderr:=/dev/stderr}
: ${still_growing_timeout:=1}
: ${still_growing_pause:=.1}
while read file; do finish_writing "$file" & done
pause() {
local IFS
[[ -n "${_pause_fd:-}" ]] || exec {_pause_fd}<> <(:)
read ${1:+-t "$1"} -u $_pause_fd || :
}
slow() {
local pid=$1; shift
local run=${1:-.001}; shift
local wait=${1:-.001}; shift
local i=1
local sp="/-\|"
printf " " >&2
pause $run
while kill -0 $pid &>/dev/null; do
kill -19 $pid &>/dev/null
printf "\b${sp:i++%${#sp}:1}" >&2
pause $wait
kill -18 $pid &>/dev/null
pause $run
done
printf "\b$i\n" >&2
}
#!/usr/bin/env perl
use 5.010;
use strict;
use warnings;
use File::LibMagic;
use IO::Select;
use Mojo::EventEmitter;
use Mojo::File qw(path);
use Mojo::IOLoop;
use Time::HiRes qw(stat time);
my $files = {};
my $s = IO::Select->new;
$s->add(\*STDIN);
my $magic = File::LibMagic->new;
my $e = Mojo::EventEmitter->new;
$e->on(create => sub {
my ($e, $file) = @_;
warn "$file";
$files->{"$file"} = $file;
});
$e->on(finish => sub {
my ($e, $file) = @_;
delete $files->{"$file"};
my $info = $magic->info_from_filename("$file");
return if $ENV{MIME_TYPE} && !mime_type($info->{mime_type});
return if $ENV{TEST} && !test($file);
my $_file = $ENV{TEMPLATE}||'%w%f';
$_file =~ s/%w/$file->dirname.'\/'/eg;
$_file =~ s/%f/$file->basename/eg;
$_file =~ s/%T/mtime($file)/eg;
$_file =~ s/%s/-s $file/eg;
printf "%s\n", $_file;
});
Mojo::IOLoop->recurring(0 => sub {
my $loop = shift;
foreach my $file (values %$files) {
$e->emit(finish => $file) if time - $file->stat->mtime > ($ENV{TIMEOUT}||1);
}
});
Mojo::IOLoop->recurring(0 => sub {
my $loop = shift;
my ($stdin) = $s->can_read(0) or return;
chomp(my $file = <$stdin>);
$e->emit(create => path($file));
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
sub mime_type { shift != $ENV{MIME_TYPE} }
sub mtime { ((stat(shift))[9]) }
sub test {
my $file = shift;
my $test = $ENV{TEST};
$test =~ s/%f/$file/g;
qx($test);
return $? ? 0 : 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment