18

I want to automatically kick off a build whenever a file changes.

I've used autospec (RSpec) in Ruby and loved that.

How can this be done in bash?

Ian Vaughan
  • 20,211
  • 13
  • 59
  • 79

6 Answers6

15

Take a look at incron and inotify-tools.

Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
  • In particular, see https://github.com/tartley/rerun2 which uses inotify-tools and makes it easy to run a custom command, as explained in [Jonathan Hartley's overlooked answer on superuser](http://superuser.com/a/970780/60931) – nealmcb Nov 23 '15 at 16:17
6

keywords are inotifywait & inotifywatch commands

zed_0xff
  • 32,417
  • 7
  • 53
  • 72
6

After reading replies to other posts, I found a post (now gone), I created this script :-

#!/bin/bash

sha=0
previous_sha=0

update_sha()
{
    sha=`ls -lR . | sha1sum`
}

build () {
    ## Build/make commands here
    echo
    echo "--> Monitor: Monitoring filesystem... (Press enter to force a build/update)"
}

changed () {
    echo "--> Monitor: Files changed, Building..."
    build
    previous_sha=$sha
}

compare () {
    update_sha
    if [[ $sha != $previous_sha ]] ; then changed; fi
}

run () {
    while true; do

        compare

        read -s -t 1 && (
            echo "--> Monitor: Forced Update..."
            build
        )

    done
}

echo "--> Monitor: Init..."
echo "--> Monitor: Monitoring filesystem... (Press enter to force a build/update)"
run
Ian Vaughan
  • 20,211
  • 13
  • 59
  • 79
  • 11
    this is the most horrible solution. it will raed all files from a directory every second which is a huge performance hit. please look at the solution by dennis williamson!!! – kritzikratzi May 23 '12 at 08:04
  • 3
    Please remove your downvotes from my answers, they are valid answers and work, just because they are not acceptable for your usage, they have been fine for mine, and possibly other people as well. Thus downvoting means others will not try this answer. Downvotes are really only for things that do not work at all or are off topic etc. And also please edit your comments removing the word horrible, its a personal option of yours, you're welcome to say its not performance hit, but leave it at that. Cheers. – Ian Vaughan May 03 '13 at 09:45
  • 5
    Because something works does not mean it's a good idea to do it. In my opinion your answer is equally trivial and problematic. I see no reason to remove my downvote. – kritzikratzi May 04 '13 at 14:29
  • 1
    Ok, its your call, whatever you choose, but may I point out http://stackoverflow.com/privileges/vote-down "Use your downvotes whenever you encounter an egregiously sloppy, no-effort-expended post, or an answer that is clearly and perhaps dangerously incorrect.". I would argue that its not sloppy nor no-effort-expended, and its certainly not dangerously incorrect. If you still believe otherwise, than thats fine, thanks. – Ian Vaughan May 08 '13 at 11:55
  • 5
    i actually double checked the vote-down guidlines before posting my last comment. – kritzikratzi May 08 '13 at 16:15
  • I'm going to do some performance testing on this solution, I think I worked somewhere that did something similar, and I feel like it's not that terrible of a hit, but everything is relative. – NateDSaint Oct 23 '13 at 23:56
  • I know it is not optimal solution but on some systems you can't use or install inotify but you have a shell... in this case we can should anything that works. Thanks Ian! – MilMike Jul 07 '16 at 08:28
5

How about this script? Uses the 'stat' command to get the access time of a file and runs a command whenever there is a change in the access time (whenever file is accessed).

#!/bin/bash
while true
do
   ATIME=`stat -c %Z /path/to/the/file.txt`
   if [[ "$ATIME" != "$LTIME" ]]
   then
       echo "RUN COMMNAD"
       LTIME=$ATIME
   fi
   sleep 5
done
Ian Vaughan
  • 20,211
  • 13
  • 59
  • 79
VDR
  • 2,663
  • 1
  • 17
  • 13
  • 2
    Well, we can use **stat** command to access/monitor DIR access time also: **stat -c %Z /var/tmp** script triggers whenever there is any addition or deletion in the DIR – VDR Aug 21 '13 at 14:28
  • I really like the simplicity of this solution. – Richard May 12 '17 at 00:36
3

See this example as an improvement upon on Ian Vaughan's answer:

#!/usr/bin/env bash
# script:  watch
# author:  Mike Smullin <mike@smullindesign.com>
# license: GPLv3
# description:
#   watches the given path for changes
#   and executes a given command when changes occur
# usage:
#   watch <path> <cmd...>
#

path=$1
shift
cmd=$*
sha=0
update_sha() {
  sha=`ls -lR --time-style=full-iso $path | sha1sum`
}
update_sha
previous_sha=$sha
build() {
  echo -en " building...\n\n"
  $cmd
  echo -en "\n--> resumed watching."
}
compare() {
  update_sha
  if [[ $sha != $previous_sha ]] ; then
    echo -n "change detected,"
    build
    previous_sha=$sha
  else
    echo -n .
  fi
}
trap build SIGINT
trap exit SIGQUIT

echo -e  "--> Press Ctrl+C to force build, Ctrl+\\ to exit."
echo -en "--> watching \"$path\"."
while true; do
  compare
  sleep 1
done
kenorb
  • 155,785
  • 88
  • 678
  • 743
Mike S
  • 51
  • 4
3

If you've entr installed, then in shell you can use the following syntax:

while true; do find src/ | entr -d make build; done
kenorb
  • 155,785
  • 88
  • 678
  • 743