Watch Filesystem in Real Time on OS X and Ubuntu
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.
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.
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.