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


6 Answers 6

watch 'command | othertool | yet-another-tool'
  • 4
    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, 2016 at 18:08
  • 1
    @sourcejedi I haven't really figured out why, but this won't always produce the same results. Oct 16, 2018 at 1:25
  • An example is ps aux | grep something and watch 'ps aux | grep something', they are not producing the same output.
    – Zitrax
    Aug 20, 2021 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, 2021 at 14:14
  • Startlingly, watch injects "sh -c" normally, to run any command
    – rogerdpack
    Oct 15, 2021 at 17:24
watch -n 1 "ls -lrt | tail -n20; date"

let's you pipe and run in a row.

  • 1
    what is -n 1 for?
    – Nam G VU
    Mar 8, 2019 at 4:50
  • 6
    @NamGVU: Run the command every 1 second
    – Duc Tran
    Mar 13, 2019 at 18:46

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

watch -n 1 "links -dump | grep -e '\S' -Fe 'www.'"
  • Or vice-versa, or escape them, but works, thanks!
    – rogerdpack
    Oct 15, 2021 at 17:45

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 {} \; '

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" ")'

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.

  • Neither watch -x 'ls -ltr | tail -n 10' nor watch -x ls -ltr | tail -n 10 work! Nov 15, 2020 at 10:30
  • Maybe you are right. -x can be useful to execute something instead of sh -c. For your purpose you can just go with watch 'ls -ltr | tail -n 10'
    – amo
    Nov 16, 2020 at 11:21

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.