Bash – Why does watch not show anything on screen when invoked with timeout in an script

bashmacoswatch

When I run this script, it does not display the watch output on the screen, and does not timeout after 5s.

§ cat script.sh
#!/bin/bash
timeout 5s watch -n 1 ps

This is what I see on the screen when I run the script:

enter image description here

However, running the content of the script directly in the terminal works as expected: i.e.,

§ timeout 5s watch -n 1 ps

What exactly is happening under the covers that is preventing watch from showing the content on the screen when invoked from a script?

Furthermore, using the --foreground option of timeout in the script makes it work as expected, but I don't understand why.

If it matters, here are versions of the tools:

§ bash --version
bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin19)
Copyright (C) 2007 Free Software Foundation, Inc.

§ watch -v
watch from procps-ng 3.3.12-dirty

§ timeout --version
timeout (GNU coreutils) 8.29
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Padraig Brady.

Best Answer

The behavior of timeout is different when it is run from a shell script. The --foreground option forces the default "interactive" behavior, even when run from a script. From the manpage:

--foreground

    when not running timeout directly from a shell prompt, allow COMMAND to
    read from the TTY and get TTY signals; in this mode, children of
    COMMAND will not be timed out

And watch needs to write to the terminal in order to do all of its fancy ANSI tricks.

If you want to see that in action, try the following:

$ script -c 'timeout --foreground 5s watch -n 1 ps'
$ less typescript
Related Question