213

I usually use watch Linux utility to watch the output of a command repeatedly every n seconds, like in watch df -h /some_volume/.

But I seem not to be able to use watch with a piped series of command like:

$ watch ls -ltr|tail -n 1

If I do that, watch is really watching ls -ltr and the output is being passed to tail -n 1 which doesn't output anything.

If I try this:

$ watch (ls -ltr|tail -n 1)

I get

$ watch: syntax error near unexpected token `ls'

And any of the following fails some reason or another:

$ watch <(ls -ltr|tail -n 1)

$ watch < <(ls -ltr|tail -n 1)

$ watch $(ls -ltr|tail -n 1)

$ watch ls -ltr|tail -n 1)

And finally if do this:

$ watch echo $(ls -ltr|tail -n 1)

I see no change in the output at the given interval because the command inside $() is run just once and the resulting output string is always printed ("watched") as a literal.

So, how do I make the watch command work with a piped chain of commands [other that putting them inside a script]?

8 Answers8

341
watch 'command | othertool | yet-another-tool'
DopeGhoti
  • 76,081
  • 6
    Might be worth noting the generic approach as well watch sh -c 'command | etc' particularly looking at the approaches tried in the question. – sourcejedi Oct 25 '16 at 18:08
  • 1
    @sourcejedi I haven't really figured out why, but this won't always produce the same results. – Michael Mior Oct 16 '18 at 01:25
  • An example is ps aux | grep something and watch 'ps aux | grep something', they are not producing the same output. – Zitrax Aug 20 '21 at 12:50
  • 1
    If you are curious about that, open it as a new question. For bonus points, include the differing output that you are seeing in the question. – DopeGhoti Aug 20 '21 at 14:14
  • 1
    Startlingly, watch injects "sh -c" normally, to run any command – rogerdpack Oct 15 '21 at 17:24
  • 1
    use -x option to use /bin/bash -c instead of sh -c for things like redirection operators command2 < <(command1) – Michael Anderson Aug 02 '23 at 16:49
23
watch -n 1 "ls -lrt | tail -n20; date"

let's you pipe and run in a row.

8

Use a combination of single quotes (') and double quotes ("). For example:

watch -n 1 "links -dump 127.0.0.1/server-status | grep -e '\S' -Fe 'www.'"
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
4

If you would like to list all files in subdirectories too, you can use find command with exec option.

watch will update every 30 seconds and find will search for all *.log files in current dir (subdirs included) and will print filenames and their last 10 line:

watch -n30 'find . -name "*.log" -print -exec tail -n10 {} \; '
4

Let's say that you run a command and then want to watch it; use:

ls -lrt | tail -n20; date
watch "!!"
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
JuanitoMint
  • 141
  • 3
3

I have just made a little bit more complex pipe chain for watching ElasticSearch nodes' performance. It requires handling variables as well as variable escaping. Hope it may be helpful to others.

watch -dn1 'kubectl exec elasticsearch-master-0 -c elasticsearch -- \
  curl -s XGET "localhost:9200/_cat/nodes?h=hp,hc,hm,rp,r,rm,cpu,dup,du,dt,m,n&v" |
  (read -r; printf "%s\n" "$REPLY"; awk "{print \$NF,\$0}" |
  sort | cut -f2- -d" ")'
2

The simplest way is using inbuilt option -x,

watch -n 5 -x tail -4 output.log  
watch -n 2 -x ls

The first eg. will display the last 4 lines of output.log file every 5 sec., second eg list content every 2 sec.

amo
  • 37
0

With a very complex command, with mixed single- and double- quotes, it may be better to save the command as a script, and execute that.

Create myCommand.sh

vi myCommand.sh

Content:

#!/bin/bash
# My long command to watch
date; echo "Log files (some in progress):  $(ls -1 */*log | wc -l)"; echo "Current Results: "; grep -h -Po "Log entry: Command 'contains single quotes in string.*" */*log | sort | uniq -c

Make executable

chmod a+x myCommand.sh

The execute it with watch

watch -n 30 ./myCommand.sh