I recently learned about watch, but am having trouble making it work with relatively sophisticated commands.

For example, I would like to ask watch to run the following command on zsh every three seconds*:

for x in `command_1 | grep keyword | cut -d' ' -f1`; do command_2 "word[word=number]" $x; done

as you can see the line above includes single quotes, double quotes, among other special characters.

So I tried:

watch -n 3 "for x in `my_command | grep keyword | cut -d' ' -f1`; do command2 "rusage[mem=7000]" $x; done"

but then I got:

no matches found for x in !@#$# ....; done

I tried other combinations without success. Here's one of those attempts:

watch -n 3 "for x in $(bjobs -w | grep pre_seg | cut -d' ' -f1); do bmod -R "rusage[mem=7000]" $x; done"

which also results in a similar error.

Any ideas how to make this work?

*I would also be intersted in solutions that work on bash


General tip: if you have two levels of nesting, avoid using single quotes in the inner command, and use single quotes around the outer command.

Additional tip: don't use backticks - `…` - to execute code, instead use $(…) around it. Dollar-parentheses is pretty much DWIM('Do what I mean') when it comes to nested quotes; backquotes have arcane, shell-dependent rules.

watch -n 3 'for x in $(my_command | grep keyword | cut -d" " -f1); do command2 "rusage[mem=7000]" "$x"; done'

If you need single quotes inside a single-quoted command, you can use '\''. Think of these four characters as the way to quote a single quote inside single quotes, though technically speaking this is constructed as end the single-quoted string, append a literal single quote, and start a new single-quoted string (still appended to the current word).

For more complex cases, either painstakingly count the quotes, or define temporary variables.

cmd='for x in $(my_command | grep keyword | cut -d" " -f1); do command2 "rusage[mem=7000]" "$x"; done'
watch_cmd='watch -n 3 "$cmd"'

This answer isn't specific to zsh. Zsh doesn't bring anything major here. You can save a bit of quoting because there's no need for double quotes around command substitutions, and sometimes there are ways to use built-ins rather than external commands which reduce the quoting needs, but the underlying issues are the same as in other shells.

Oh, and by the way, note that watch will execute your command in sh, not in zsh. If you want to execute the command in zsh, you need to run

watch -n 3 -x zsh -c "$cmd"

on Debian/Ubuntu, and

export cmd
watch -n 3 'exec zsh -c "$cmd"'

(even more quoting!) elsewhere.

| improve this answer | |
  • Thanks @Gilles. That was very helpful. Interestingly watch does not come with the -x nor -c options on my machine. I looked it up online and haven't found any man pages that mention them. What do these options do? – Amelio Vazquez-Reina Oct 26 '11 at 21:45
  • 1
    @intrpc -x tells watch not to pass the command through a shell. I just found out that this is specific to Debian/Ubuntu, even though it's not indicated as such. The -c is passed to zsh, not to watch. – Gilles 'SO- stop being evil' Oct 26 '11 at 22:03
  • @Gilles The -x and -exec options both exist in my watch (on gentoo), so this is definitely not Debian-specific. Maybe you compared with some other version of watch? Mine comes from procps package. – rozcietrzewiacz Oct 27 '11 at 6:46
  • 1
    @rozcietrzewiacz watch comes from procps on Debian too. The official source doesn't have --exec. The package in Debian (and derivatives including Ubuntu) adds the option in a Debian-specific patch (watch_exec_beep.patch; it's “Mortys watch exec patch” from bug #410967). Gentoo may have adopted a similar patch. – Gilles 'SO- stop being evil' Oct 27 '11 at 7:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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