-
Notifications
You must be signed in to change notification settings - Fork 6
/
vnl-tail
executable file
·193 lines (134 loc) · 5.12 KB
/
vnl-tail
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use autodie;
use FindBin '$RealBin';
use lib "$RealBin/lib";
use Vnlog::Util qw(parse_options read_and_preparse_input ensure_all_legends_equivalent close_nondev_inputs reconstruct_substituted_command);
# This comes from the struct option long_options in tail.c in GNU coreutils
my @specs = ( # options with no args
"silent|quiet|q",
"zero-terminated|z",
"verbose|v",
"retry",
"F",
"f",
# options with args
"bytes|c=s",
"lines|n=s",
"sleep-interval|s=f",
"max-unchanged-stats=i",
"pid=i",
# '--follow' and '--follow=...' are valid. '--follow ...' is NOT
# valid. parse_options handles that
"follow:s",
"vnl-tool=s",
"help|h");
my %options_unsupported = ( 'bytes' => <<'EOF',
vnlog is line-oriented, so I refuse to split lines
EOF
'zero-terminated' => <<'EOF'
vnlog is built on assuming a particular record separator
EOF
);
my ($filenames,$options) = parse_options(\@ARGV, \@specs, 0, <<EOF);
$0 [options] logfile logfile logfile ... < logfile
The most common options are (mostly from the GNU tail manpage)
-n, --lines=[+]NUM
output the last NUM lines, instead of the last 10;
or use -n +NUM to output starting with line NUM
-f, --follow[={name|descriptor}]
output appended data as the file grows;
an absent option argument means 'descriptor'
--vnl-tool tool
Specifies the path to the tool we're wrapping. By default we wrap 'tail',
so most people can omit this
EOF
$options->{'vnl-tool'} //= 'tail';
if( defined $options->{follow} &&
length($options->{follow}) > 0 &&
$options->{follow} ne 'name' &&
$options->{follow} ne 'descriptor' )
{
die "--follow can be given ONLY as --follow=name or --follow=descriptor";
}
for my $key(keys %$options)
{
if($options_unsupported{$key})
{
my $keyname = length($key) == 1 ? "-$key" : "--$key";
die("I don't support $keyname: $options_unsupported{$key}");
}
}
my $inputs = read_and_preparse_input($filenames,
# read all lines, including comments
['cat']);
ensure_all_legends_equivalent($inputs);
say '# ' . join(' ', @{$inputs->[0]{keys}});
# When looking at normal files, tail needs the original filenames to be able to
# do its thing. A pipe filter won't do; especially if we're doing a tail -f. But
# if the thing I'm tailing is a pipe to begin with, I need to give it that pipe.
# The NAME of the device won't do. Thus I call reconstruct_substituted_command()
# telling it to substitute the dev filenames, but leave non-dev filenames alone.
#
# Additionally, since I don't use the file filter anymore, I close those pipes
my $ARGV_new = reconstruct_substituted_command($inputs, $options, [], \@specs, 1);
close_nondev_inputs($inputs);
exec $options->{'vnl-tool'}, @$ARGV_new;
__END__
=head1 NAME
vnl-tail - tail a log file, preserving the legend
=head1 SYNOPSIS
$ read_temperature | tee temp.vnl
# temperature
29.5
30.4
28.3
22.1
... continually produces data
... at the same time, in another terminal
$ vnl-tail -f temp.vnl
# temperature
28.3
22.1
... outputs data as it comes in
=head1 DESCRIPTION
Usage: vnl-tail [options] logfile logfile logfile ... < logfile
This tool runs C<tail> on given vnlog files in various ways. C<vnl-tail> is a
wrapper around the GNU coreutils C<tail> tool. Since this is a wrapper, most
commandline options and behaviors of the C<tail> tool are present; consult the
L<tail(1)> manpage for detail. The differences from GNU coreutils C<tail> are
=over
=item *
The input and output to this tool are vnlog files, complete with a legend
=item *
C<-c> is not supported because vnlog really doesn't want to break up lines
=item *
C<--zero-terminated> is not supported because vnlog assumes
newline-separated records
=item *
By default we call the C<tail> tool to do the actual work. If the underlying
tool has a different name or lives in an odd path, this can be specified by
passing C<--vnl-tool TOOL>
=back
Past that, everything C<tail> does is supported, so see that man page for
detailed documentation.
=head1 COMPATIBILITY
I use GNU/Linux-based systems exclusively, but everything has been tested
functional on FreeBSD and OSX in addition to Debian, Ubuntu and CentOS. I can
imagine there's something I missed when testing on non-Linux systems, so please
let me know if you find any issues.
=head1 SEE ALSO
L<tail(1)>
=head1 REPOSITORY
https://github.com/dkogan/vnlog/
=head1 AUTHOR
Dima Kogan C<< <[email protected]> >>
=head1 LICENSE AND COPYRIGHT
Copyright 2018 Dima Kogan C<< <[email protected]> >>
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2.1 of the License, or (at your option) any
later version.
=cut