Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I'm looking for a CLI tool which will watch a directory and spit out the names of files that change in real time.

some_watch_command /path/to/some/folder | xargs some_callback

I'm aware of inotify (inotify-tools?) and it seems to be what I need, but I need something that is both Linux (in my case Ubuntu) and OSX compatible.

It doesn't need to be lightning fast, but it does need to trigger upon changes (within a second is reasonable). Also, I don't necessarily need the exact CLI program mentioned above. If some underlying tech exists and is easily scriptable on both platforms that would be great too.

share|improve this question
    
I like fswatch for OS X which I found on a question that was tagged as a duplicate - superuser.com/questions/445907/… –  cwd Aug 28 '12 at 19:12

3 Answers 3

up vote 6 down vote accepted

OS X would typically use Folder Actions or Launchd for this task.

The only cross-platform tool I know is the watchdog API for Python (thus available for OS X and Linux). Install it through pip (or easy_install)

pip install watchdog

It comes with the watchmedo command line tool, which allows you to run shell commands on system events. Check its general syntax with watchmedo --help

Here is a short example, where you could easily modify the command and the path highlighted in bold:

watchmedo shell-command \
    --recursive \
    --command='echo "${watch_src_path}"' \
    /some/folder

This would simply spit out the paths to all changed files or folders, allowing you to pipe watchmedo's output to another shell command.

share|improve this answer
    
Looks like Folder Actions have some limitations (and are hard to test without OSX), but watchmedo is working perfectly for me. Thanks! –  Adrian Schneider Jun 4 '12 at 23:53

I've tweaked some Ruby scripts I've come across to do exactly what you're looking for. Here's the Ruby code:

#!/usr/bin/ruby
# Inspired by http://vikhyat.net/code/snippets/#watchfile

# How to use:
# This script takes two paramaters:  a folder and a shell command
# The script watches for when files in the folder are changed.  When they are, the shell command executes
# Here are some shortcuts you can put in the shell script:
# %F = filename (with extension)
# %B = base filename (without extension)


unless ARGV.length == 2
  puts "\e[32m    Usage: \e[0mruby OnSaveFolder.rb 'folder' 'command'"
  exit
end

require 'digest/md5'
require 'ftools'

$md5 = ''
$directory = File.expand_path(ARGV[0])
$contents = `ls #{$directory}`
$contentsList = $contents.split("\n")
$fileList = []


Dir.chdir($directory)

for i in $contentsList
  if ( File.file?(File.expand_path(i)) == true)
    $fileList.push(i)
  end
end

$processes = []

def watch(file, timeout, &cb)
  $md5 = Digest::MD5.hexdigest(File.read(file))
  loop do
    sleep timeout
    if ( temp = Digest::MD5.hexdigest(File.read(file)) ) != $md5
      $md5 = temp
      cb.call(file)
    end
  end
end

puts "\e[31m Watching files in folder \e[34m #{$directory}"
puts "\e[32m    Press Control+C to stop \e[0m"

for i in $fileList
  pid = fork do
    $num = 0
    $filePath = File.expand_path(i)

    watch i, 1 do |file|
      puts "\n     #{i} saved"

      $command = ARGV[1].dup
      if ( ARGV[1].include?('%F') )
        $command = $command.gsub!('%F', i)
      end
      if ( ARGV[1].include?('%B') )
        $command = $command.gsub!('%B', File.basename(i, '.*'))
      end

      $output = `#{$command}`
      if ( $output != '')
        puts "\e[34m     #{$command}\e[31m output: \e[0m"
        puts $output
      end
      puts "\e[34m     #{$command}\e[31m finished \e[0m (#{$num}, #{Time.now})\n"
      $num += 1

    end
  end
  $processes.push(pid)
end

Process.wait(pid)

for i in $processes
  `kill #{i}`
end

I named this script "OnSaveFolder.rb". It takes two parameters: the folder you want to watch for changes, and the bash code you want to run when there's been a change. For example,

ruby OnSaveFolder.rb '/home/Movies' 'echo "A movie has been changed!"'

I hope this helps! I've found that ruby works very well for this type of thing, and it's installed on OS X by default.

share|improve this answer
    
Gave it a shot but a little hard to tweak without any Ruby background. Thanks anyway! –  Adrian Schneider Jun 4 '12 at 23:54
    
this is great. any idea how to make it recursively watch folders below ? –  Nate Flink Jul 10 '13 at 19:35
    
If I don't care about the execution of the command on change, would this be multi-platform, or only unix? And is it actually real time? Some file watchers out there has a ridiculous delay (minutes). –  Cort3z Dec 9 '13 at 11:47

You could put lsof into a watch command and have it update every <s> seconds:

watch -n <s> lsof

and launch that with whatever filter, such as watching pid1, pid2, pid3 and ignoring pid4

lsof -p "pid1,pid2,pid3,^pid4"

If that's not enough, you could always write your own filters with grep.

For OS X, see this question's answers.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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