0

I want to execute a program periodically and watch when something changes. It sounds like watch would be the right command for that but it prints out annoying empty lines which makes it impossible to see the history. The -t switch does not really help here.

What i want:

user@machine ~$ #some previous command
user@machine ~$ watch -t date -Is
2020-10-09T12:50:13
2020-10-09T12:50:15
2020-10-09T12:50:17

What i get:

2020-10-09T12:50:17




(all the previous lines are gone).

I know i can use a while loop with a sleep command, but this takes longer to type and has other disadvantages. This loop: while true; do date -Is ; sleep 2; done works but i would prefer a simple command. I also read about a trick using tee, like this watch -t ' date -Is | tee -a ./somefile, but that is not always a viable solution for me since this only works with a writable filesystem and if it is a tempfs i can not read it after a reboot, which is a problem on a embedded system where i sometimes have no writable file system mounted and i can't read a tempfs after a crash/reboot.

I also tried to filter out empty lines with grep: watch -t date -Is | grep -v '^$' but then i have no output.

3
  • Don't the previous lines appear as soon as you exit watch? Commented Oct 6, 2020 at 13:40
  • watch uses ncurses-type terminal codes to rewrite the outputs. The output characters are disjoint, TERM-dependent, not arranged in lines, and not stored anywhere. Commented Oct 6, 2020 at 14:31
  • watch also breaks your terminal settings occasionally, requiring a reset. Commented Oct 6, 2020 at 14:34

2 Answers 2

2

A bit convoluted, but you could do:

yes 'date -Is' | pv -qlL1 | sh

(also has the advantage to run the command every second, instead of every 2 seconds plus however long it takes to run it)

1 above is the rate in number of lines per second and can only be an integer number. So 1 per second is the slowest you can get. You could drop the -l though in which case, the rate would be in number of bytes per second. So at 9 bytes per line, that'd be one command every 9 seconds.

8
  • Interesting side-effect: yes instantly fills the pipe, so if you kill yes the pipeline continues for a couple of hours (64KB of commands). Now I need to find a use for that. Commented Oct 6, 2020 at 15:01
  • @Paul_Pedant why is it that interesting? What were you expecting? Commented Oct 7, 2020 at 4:06
  • @Căcărău I'm not familiar with pv: intuitively, I expected the throttling to be be pre-emptive. Also, ulimit says my pipe size is 4KB, but experiment says 64KB. Commented Oct 7, 2020 at 10:56
  • 1
    @Paul_Pedant, you may be refering to bash's ulimit -p. I believe on Linux it reports PIPE_BUF which is the maximum amount of data that write()s is guaranteed to write atomically (not much to do with the size of pipes). On Linux, the default size of pipes is 64KiB (up from 4KiB since 2.6.11), though can be changed dynamically on a per-pipe basis. Commented Oct 7, 2020 at 11:34
  • @Paul_Pedant how could pv preempt the process writing to the other end of the pipe? Commented Oct 15, 2020 at 7:15
1
perl -e '$t=shift; while(1){print $o=$n if ($n=qx(@ARGV)) ne $o; sleep $t}' 1 date -Is
2020-10-07T06:49:07+03:00
2020-10-07T06:49:08+03:00
2020-10-07T06:49:09+03:00
2020-10-07T06:49:10+03:00

An improved version which does fractional delays and correctly handles spaces and special characters in the command & args:

#! /usr/bin/perl
use Time::HiRes qw(sleep);
die "usage: $0 delay cmd [args ...]\n" if @ARGV < 2;
sub slurpcmd {
   open my $h, '-|', @_ or die "open $_[0]|: $!";
   local $/ unless wantarray;
   <$h>
}
my $t = shift; my ($o, $n);
while(1){print $o=$n if ($n=slurpcmd @ARGV) ne $o; sleep $t}

Adding options to format the output of the command (nicely separate it if multiline, etc) is left as exercise to the reader ;-)

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.