Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm trying to watch number of files in my /tmp/ directory. For this I thought this command would work:

watch sh -c 'ls /tmp/|wc -l'

But it appears to work as if ls had no arguments. Namely, I'm in ~, and I get number of files there instead of /tmp/. I found a workaround, which seems to work:

watch sh -c 'ls\ /tmp/|wc -l'

But why do I need to escape the space between ls and /tmp/? How is the command transformed by watch so that ls output is feeded to wc, but /tmp/ is not passed as argument to ls?

share|improve this question
    
watch "sh -c 'ls /tmp | wc -l'" doing this command should get the desired affect. This isn't watches fault, try sh -c ls /tmp and you'll get your home directory (but I have no idea why...) – Jacob Minshall Jan 29 at 19:50
8  
Not an answer but you're using watch incorrectly .The command that you pass to watch is in turn fed by watch to sh -c, so you're in effect doing sh -c twice. – iruvar Jan 29 at 19:51
    
If you are curious you can also have a look at the source. – michas Jan 29 at 20:21
    
@JacobMinshall, the why is straightforward: The /tmp is an argument to sh, in that case, not an argument to ls. – Charles Duffy Jan 30 at 4:40
up vote 17 down vote accepted

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' ?

share|improve this answer
    
Oh indeed, didn't think of trying to strace it. – Ruslan Jan 29 at 20:06
1  
Actually, ` is backquote (or back-tick).  This question is about \, which is backslash. – G-Man Jan 30 at 3:27
    
@Ruslan: I posted this comment on this answer because it is a comment on this answer.  thrig says "In the backquote case, ls /tmp is ..." and "Without this backquote, the command is ...", and uses bq and nobq as filenames, when all the while referring to the backslash in your ls\ /tmp command. – G-Man Jan 31 at 18:35

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'
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.