Why does `watch` make `ls /tmp` list contents of $HOME?
The difference may be seen via strace
:
$ strace -ff -o bq watch sh -c 'ls\ /tmp/|wc -l'
^C
$ strace -ff -o nobq watch sh -c 'ls /tmp/|wc -l'
^C
$ grep exec bq* | grep sh
bq.29218:execve("/usr/bin/watch", ["watch", "sh", "-c", "ls\\ /tmp/|wc -l"], [/* 54 vars */]) = 0
bq.29219:execve("/bin/sh", ["sh", "-c", "sh -c ls\\ /tmp/|wc -l"], [/* 56 vars */]) = 0
bq.29220:execve("/bin/sh", ["sh", "-c", "ls /tmp/"], [/* 56 vars */]) = 0
$ grep exec nobq* | grep sh
nobq.29227:execve("/usr/bin/watch", ["watch", "sh", "-c", "ls /tmp/|wc -l"], [/* 54 vars */]) = 0
nobq.29228:execve("/bin/sh", ["sh", "-c", "sh -c ls /tmp/|wc -l"], [/* 56 vars */]) = 0
nobq.29229:execve("/bin/sh", ["sh", "-c", "ls", "/tmp/"], [/* 56 vars */]) = 0
In the backquote case, ls /tmp
is passed as a single argument to the -c
to sh
, which runs as expected. Without this backquote, the command is instead word split when watch
runs sh
which in turn runs the supplied sh
, so that only ls
is passed as the argument to -c
, meaning that the sub-subsh
will only run a bare ls
command, and lists the contents of the current working directory.
So, why the complication of sh -c ...
? Why not simply run watch 'ls /tmp|wc -l'
?
There are two main categories of watch
commands (of the ones that are to run commands periodically, watch
is not a standard command, there are even systems where watch
does something completely different like snooping on another tty line on FreeBSD).
One that already passes the concatenation of its arguments with spaces to a shell (it does in effect call sh -c <concatenation-of-arguments>
) and one that just runs the command specified with the arguments specified without invoking a shell.
You're in the first situation, so you just need:
watch 'ls /tmp/|wc -l'
When you do:
watch sh -c 'ls /tmp/|wc -l'
your watch
actually runs:
sh -c 'sh -c ls /tmp/|wc -l'
And sh -c ls /tmp/
is running the ls
inline script where $0
is /tmp/
(so ls
is run without arguments and lists the current directory).
Some of the watch
implementations in the first category (like the one from procps-ng on Linux) accept a -x
option to make them behave like the watch
of the second category. So with there, you can do:
watch -x sh -c 'ls /tmp/|wc -l'