14

Does watch only monitor the visible output of a command? Say I'm in a directory with the following contents:

$ ls
a  b  c  d  e  f  g  h  i  j  k  l  m  n

If I run watch -g ls -1 I expect it to exit if a file is added or removed. What actually happens is that it exits only if the file in question is visible in the terminal output of watch:

$ watch -g ls -1
Every 2.0s: ls -1                   Wed Nov 13 16:35:03 2013

a
b
c
d
e
f

Deleting the file m, which is not visible because of the size of my terminal, does nothing. Deleting a visible file, say d, causes watch to exit as expected.

The -g flag is explained thusly in my man page:

   -g, --chgexit
          Exit when the output of command changes.

What's going on? Is this normal? How can I use watch for commands with long output? I am using watch from procps-ng 3.3.4 which was installed from the Debian repos.

terdon
  • 242,166
  • What does the -g option to watch do? I don't find it in my version of watch – iruvar Nov 13 '13 at 16:09
  • @1_CR see updated question, it should cause it to exit when the output changes. It does work as advertised when the change is visible on the screen. – terdon Nov 13 '13 at 16:18

2 Answers2

11

I found this thread titled: Bug#225549: have watch monitor stderr. That thread is from 2008, but it looks like older versions don't support the watching of anything other than STDOUT.

So we're limited to just STDOUT. As for visible there is a lot of language in the info watch and man watch that make me think your observation/assumption is correct.

excerpt

   watch runs command repeatedly, displaying its output (the first screen‐
   full).   This  allows you to watch the program output change over time.
   By default, the program is run every 2 seconds; use -n or --interval to
   specify a different interval.

Also this bit under BUGS:

BUGS
       Upon  terminal resize, the screen will not be correctly repainted until
       the next scheduled update.  All --differences highlighting is  lost  on
       that update as well.

If I had to guess I'd think they were storing the visible bits in a buffer between runs, and then analyzing just those characters.

EDIT #1

I debugged this further using strace and you can see watch reading the output from the ls command so it's internally dropping the change.

before I delete the m file

$ strace -o w.log watch -g 'ls -1'
read(3, "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\nw.lo"..., 4096) = 34
close(3)                                = 0
--- SIGCHLD (Child exited) @ 0 (0) ---
munmap(0x7f4da83af000, 4096)            = 0
wait4(31011, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 31011
rt_sigaction(SIGTSTP, {SIG_IGN, [], SA_RESTORER|SA_RESTART, 0x7f4da79b94a0}, {0x7f4da7f81ee0, [], SA_RESTORER|SA_RESTART, 0x7f4da79b94a0}, 8) = 0
write(1, "\33[H\33[2JEvery 2.0s: ls -1\33[1;140H"..., 119) = 119
rt_sigaction(SIGTSTP, {0x7f4da7f81ee0, [], SA_RESTORER|SA_RESTART, 0x7f4da79b94a0}, NULL, 8) = 0
nanosleep({2, 0}, NULL)                 = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
pipe([3, 4])                            = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f4da839f9d0) = 31014
close(4)                                = 0
fcntl(3, F_GETFL)                       = 0 (flags O_RDONLY)
fstat(3, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4da83af000
lseek(3, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
read(3, "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\nw.lo"..., 4096) = 34
close(3)                                = 0
munmap(0x7f4da83af000, 4096)            = 0
--- SIGCHLD (Child exited) @ 0 (0) ---

after the m file is deleted

--- SIGCHLD (Child exited) @ 0 (0) ---
rt_sigaction(SIGTSTP, {SIG_IGN, [], SA_RESTORER|SA_RESTART, 0x7f4da79b94a0}, {0x7f4da7f81ee0, [], SA_RESTORER|SA_RESTART, 0x7f4da79b94a0}, 8) = 0
poll([{fd=0, events=POLLIN}], 1, 0)     = 0 (Timeout)
poll([{fd=0, events=POLLIN}], 1, 0)     = 0 (Timeout)
write(1, "\33[1;158H8\33[11;163H", 18)  = 18
rt_sigaction(SIGTSTP, {0x7f4da7f81ee0, [], SA_RESTORER|SA_RESTART, 0x7f4da79b94a0}, NULL, 8) = 0
nanosleep({2, 0}, NULL)                 = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
pipe([3, 4])                            = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f4da839f9d0) = 31028
close(4)                                = 0
fcntl(3, F_GETFL)                       = 0 (flags O_RDONLY)
fstat(3, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4da83af000
lseek(3, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
read(3, "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nn\nw.log\n", 4096) = 32
close(3)                                = 0
--- SIGCHLD (Child exited) @ 0 (0) ---
munmap(0x7f4da83af000, 4096)            = 0
wait4(31028, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 31028
terdon
  • 242,166
slm
  • 369,824
  • Yeah, it just seems weird, it makes it impossible to run something like watch -g foo; echo "Something changed!". It seems a strangely crippling bug in such an established program. – terdon Nov 14 '13 at 01:50
  • @terdon - my ancient version of Fedora didn't have the -g switch but I tried it on Ubuntu and it behaves the same. – slm Nov 14 '13 at 02:19
  • OK, that's really weird then. So it does actually monitor and see the change, it just doesn't react to it! Definitely a bug then. – terdon Nov 14 '13 at 12:15
2

I expect it to exit if a file is added or removed

I'm pretty sure you're after inotify-tools.

My manpage for watch, from procps-ng, says

watch runs command repeatedly, displaying its output and errors (the first screenfull).

jthill
  • 2,710
  • That's not what he's asking about, he's trying to understand the behavior of an update occurring, being displayed via STDOUT, but isn't visible in the terminal b/c he's re-sized it so that the output that is being changed is "off screen". Most everyone I've discussed this with today would've expected watch to behave as the OP, and exit with the change. – slm Nov 14 '13 at 07:06
  • Yes we've already discussed that too, I highlighted that same text in my answer. I know Terdon fairly well and at this point he wants to know the reason why. – slm Nov 14 '13 at 07:32
  • I agree it's not the answer to his question, but it's the solution to his problem. – jthill Nov 14 '13 at 07:33
  • You mean using inotify? That's not what he's after, he wants to know why watch behaves this way. He knows about inotify. – slm Nov 14 '13 at 07:34
  • That's his question. What he's trying to do with it is what I quoted: wait for a file to be added or removed. Watch isn't the tool for that job. – jthill Nov 14 '13 at 07:36
  • Yeah, my point is how is this any different than what I said? – slm Nov 14 '13 at 07:38
  • It isn't, I agree with what you said. But he's coming looking for how to do something he's decided is the solution to his real problem, when it isn't. – jthill Nov 14 '13 at 08:16
  • jthill, thanks but as @slm said my real question here is why this is happening. It seems like a crippling bug for such an established program and renders the -g flag useless if it only exits on visible changes. I am using ls as an example, my original use case was watching the output of a command that prints a few hundred lines. I wanted it to exit and continue on to the next command if the output changed. inotify is great but won't help with that. – terdon Nov 14 '13 at 12:12
  • @terdon then what's wrong with cmp-in-a-loop? cmd>last;while cmd>this; do cmp last this; rc=$?; mv this last; test $rc -ne 0 && break; sleep 2; done hardly seems worth writing watch to do that. – jthill Nov 14 '13 at 13:54
  • @jthill because 1) I am wondering why watch behaves like that and I consider it a bug (and have filed a bug report), it should not behave that way 2) As you can see, what you just wrote is far more complex than using watch -g command; 2ndcommand 3) watch is a basic tool and part of the procps package, it should work as advertized. – terdon Nov 14 '13 at 14:02