joseph.hainline
joseph.hainline

Reputation: 26608

OS X Bash, 'watch' command

I'm looking for the best way to duplicate the Linux 'watch' command on Mac OS X. I'd like to run a command every few seconds to pattern match on the contents of an output file using 'tail' and 'sed'.

What's my best option on a Mac, and can it be done without downloading software?

Upvotes: 488

Views: 269234

Answers (14)

Jeremiah
Jeremiah

Reputation: 81

No blinky blinky

newwatch(){while :; do "$@" > /tmp/newwatch; clear; cat /tmp/newwatch; sleep 1; done}

Upvotes: 0

Andrew
Andrew

Reputation: 3969

The watch command that's available on Linux does not exist on macOS. If you don't want to use brew you can add this bash function to your shell profile.

# execute commands at a specified interval of seconds
function watch.command {
  # USAGE: watch.commands [seconds] [commands...]
  # EXAMPLE: watch.command 5 date
  # EXAMPLE: watch.command 5 date echo 'ls -l' echo 'ps | grep "kubectl\\\|node\\\|npm\\\|puma"'
  # EXAMPLE: watch.command 5 'date; echo; ls -l; echo; ps | grep "kubectl\\\|node\\\|npm\\\|puma"' echo date 'echo; ls -1'
  local cmds=()
  for arg in "${@:2}"; do
    echo $arg | sed 's/; /;/g' | tr \; \\n | while read cmd; do
      cmds+=($cmd)
    done
  done
  while true; do
    clear
    for cmd in $cmds; do
      eval $cmd
    done
    sleep $1
  done
}

https://gist.github.com/Gerst20051/99c1cf570a2d0d59f09339a806732fd3

Upvotes: 0

seantomburke
seantomburke

Reputation: 11692

With Homebrew installed:

brew install watch

Upvotes: 930

rhs
rhs

Reputation: 1293

Use the Nix package manager!

Install Nix, and then do nix-env -iA nixpkgs.watch and it should be available for use after the completing the install instructions (including sourcing . "$HOME/.nix-profile/etc/profile.d/nix.sh" in your shell).

Upvotes: 1

tungv
tungv

Reputation: 117

To prevent flickering when your main command takes perceivable time to complete, you can capture the output and only clear screen when it's done.

function watch {while :; do a=$($@); clear; echo "$(date)\n\n$a"; sleep 1;  done}

Then use it by:

watch istats

Upvotes: 7

jon
jon

Reputation: 91

Or, in your ~/.bashrc file:

function watch {
    while :; do clear; date; echo; $@; sleep 2; done
}

Upvotes: 9

GoTLiuM
GoTLiuM

Reputation: 791

Use MacPorts:

$ sudo port install watch

Upvotes: 45

IT Gumby
IT Gumby

Reputation: 1077

The shells above will do the trick, and you could even convert them to an alias (you may need to wrap in a function to handle parameters):

alias myWatch='_() { while :; do clear; $2; sleep $1; done }; _'

Examples:

myWatch 1 ls ## Self-explanatory
myWatch 5 "ls -lF $HOME" ## Every 5 seconds, list out home directory; double-quotes around command to keep its arguments together

Alternately, Homebrew can install the watch from http://procps.sourceforge.net/:

brew install watch

Upvotes: 23

Marvin Pinto
Marvin Pinto

Reputation: 30990

I am going with the answer from here:

bash -c 'while [ 0 ]; do <your command>; sleep 5; done'

But you're really better off installing watch as this isn't very clean...

Upvotes: 12

Daniel Pittman
Daniel Pittman

Reputation: 17182

You can emulate the basic functionality with the shell loop:

while :; do clear; your_command; sleep 2; done

That will loop forever, clear the screen, run your command, and wait two seconds - the basic watch your_command implementation.

You can take this a step further and create a watch.sh script that can accept your_command and sleep_duration as parameters:

#!/bin/bash
# usage: watch.sh <your_command> <sleep_duration>

while :; 
  do 
  clear
  date
  $1
  sleep $2
done

Upvotes: 458

A_funs
A_funs

Reputation: 1246

If watch doesn't want to install via

brew install watch

There is another similar/copy version that installed and worked perfectly for me

brew install visionmedia-watch

https://github.com/tj/watch

Upvotes: 10

Robert Nagtegaal
Robert Nagtegaal

Reputation: 897

Try this:

#!/bin/bash
# usage: watch [-n integer] COMMAND

case $# in
    0)
        echo "Usage $0 [-n int] COMMAND"
        ;;
    *)      
        sleep=2;
        ;;
esac    

if [ "$1" == "-n" ]; then
    sleep=$2
    shift; shift
fi


while :; 
    do 
    clear; 
    echo "$(date) every ${sleep}s $@"; echo 
    $@; 
    sleep $sleep; 
done

Upvotes: 2

Brad Parks
Brad Parks

Reputation: 72001

Here's a slightly changed version of this answer that:

  • checks for valid args
  • shows a date and duration title at the top
  • moves the "duration" argument to be the 1st argument, so complex commands can be easily passed as the remaining arguments.

To use it:

  • Save this to ~/bin/watch
  • execute chmod 700 ~/bin/watch in a terminal to make it executable.
  • try it by running watch 1 echo "hi there"

~/bin/watch

#!/bin/bash

function show_help()
{
  echo ""
  echo "usage: watch [sleep duration in seconds] [command]"
  echo ""
  echo "e.g. To cat a file every second, run the following"
  echo ""
  echo "     watch 1 cat /tmp/it.txt" 
  exit;
}

function show_help_if_required()
{
  if [ "$1" == "help" ]
  then
      show_help
  fi
  if [ -z "$1" ]
    then
      show_help
  fi
}

function require_numeric_value()
{
  REG_EX='^[0-9]+$'
  if ! [[ $1 =~ $REG_EX ]] ; then
    show_help
  fi
}

show_help_if_required $1
require_numeric_value $1

DURATION=$1
shift

while :; do 
  clear
  echo "Updating every $DURATION seconds. Last updated $(date)"
  bash -c "$*"
  sleep $DURATION
done

Upvotes: 1

ghoti
ghoti

Reputation: 46846

It may be that "watch" is not what you want. You probably want to ask for help in solving your problem, not in implementing your solution! :)

If your real goal is to trigger actions based on what's seen from the tail command, then you can do that as part of the tail itself. Instead of running "periodically", which is what watch does, you can run your code on demand.

#!/bin/sh

tail -F /var/log/somelogfile | while read line; do
  if echo "$line" | grep -q '[Ss]ome.regex'; then
    # do your stuff
  fi
done

Note that tail -F will continue to follow a log file even if it gets rotated by newsyslog or logrotate. You want to use this instead of the lower-case tail -f. Check man tail for details.

That said, if you really do want to run a command periodically, the other answers provided can be turned into a short shell script:

#!/bin/sh
if [ -z "$2" ]; then
  echo "Usage: $0 SECONDS COMMAND" >&2
  exit 1
fi

SECONDS=$1
shift 1
while sleep $SECONDS; do
  clear
  $*
done

Upvotes: 18

Related Questions