Is there a simple way to automatically run a (Terminal) command every time a file is changed in a specific folder?
This should be possible via the command line, or a system built-in application, no third-party applications.
Any ideas?
Is there a simple way to automatically run a (Terminal) command every time a file is changed in a specific folder?
This should be possible via the command line, or a system built-in application, no third-party applications.
Any ideas?
entr(1) is a utility for running commands when files change. It reads a list of file on STDIN and uses kqueue(2) to avoid polling.
Example:
ls my_project/*.html | entr echo "file changed"
It can be installed directly from the project site or via Homebrew.
For 2022, simply % brew install entr
to install.
Save a property list like this as ~/Library/LaunchAgents/test.plist
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
http://www.apple.com/DTDs/PropertyList-1.0.dtd>
<plist version="1.0">
<dict>
<key>Label</key>
<string>test</string>
<key>ProgramArguments</key>
<array>
<string>say</string>
<string>yy</string>
</array>
<key>WatchPaths</key>
<array>
<string>~/Desktop/</string>
</array>
</dict>
</plist>
The agent can be loaded with launchctl load ~/Library/LaunchAgents/test.plist
or by logging out and back in. Applying changes requires unloading and loading the plist.
Tilde expansion (~/
) works in WatchPaths by default. EnableGlobbing adds wildcard and tilde expansion for ProgramArguments, but it doesn't affect Program or WatchPaths.
If a watched file is modified within ten seconds from the last invocation, a message like this is added to system.log:
com.apple.launchd.peruser.501[146]: (test) Throttling respawn: Will start in 7 seconds
One way to get rid of the messages is to add something like sleep 10
to the end of scripts. Setting ThrottleInterval to 10 doesn't help.
Changes in subfolders of watched folders aren't detected.
See man launchd
and man launchd.plist
for more information.
Folder actions are fine for triggering when a file is added or modified.
However, if your definition of changed includes deleting a file, OSX Folder actions does not detect if a file has been removed.
To answer the question:
Download the FileWatcher dependencies from here: https://github.com/eonist/FileWatcher
Familiarize your self with running swift in the comandline: http://krakendev.io/blog/scripting-in-swift
Use this code to watch a folder.
Code:
var fileWatcher = FileWatcher(["~/Desktop/test/".tildePath])/*<---the fileWatcher instance must be scoped to your class*/
fileWatcher!.event = { event in
Swift.print(self?.someVariable)//Outputs: a variable in your current class
Swift.print(event.description)//Outputs: a description of the file change
}
You could use Folder Actions, which lets you execute (Automator) scripts whenever contents in a folder change. As far as I know, Automator has a template that lets you easily create a new folder action and attach it to the desired folder. And by adding a "Run Shell Script" action, you should just get the desired effect.
Automator has a type of workflow called a "Folder Action" which automatically runs when something is added to a folder. Create one, then use the action "Run Shell Script."
I've found fswatch which claims to be "a cross-platform file change monitor", and seems to support all the proper platform solutions like Filesystem Events on macOS.
# Monitor all files in a directory, execute script once when something changes
fswatch -o -r path/to/directory | xargs -n1 -I{} your-command-here
You might want to try something like this:
touch /tmp/dirb.tmp
while true do
ls /thedirtocheck > /tmp/dira.tmp
diff /tmp/dira.tmp /tmp/dirb.tmp || echo "something changed"
cp /tmp/dira.tmp /tmp/dirb.tmp
sleep 100
done
ls -l
to catch file changes as well.