可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am trying to create a bash script that takes 2 parameters: a directory and a command. I need to watch this directory for changes and when something has been changed I need to execute the command. I'm really new to bash scripting and am not really sure what I am doing, so go easy on me. I'm also on a mac, not linux. Any pointers, or external resources would greatly help. I know that a lot of people try this on the internet and no one can seem to get it right. I am really trying to mimic SASS's watch functionality.
#!/bin/bash
#./watch.sh $PATH $COMMAND
DIR=$1
ls -l $DIR > $DIR/.begin
#this does not work
DIFFERENCE=$(diff .begin .end)
if [ $DIFFERENCE = '\n']; then
#files are same
else
$2
fi
ls -l $DIR > $DIR/.end
回答1:
To continuously recursively monitor folder (md5) and execute a command on change:
daemon() {
chsum1=""
while [[ true ]]
do
chsum2=`find src/ -type f -exec md5 {} \;`
if [[ $chsum1 != $chsum2 ]] ; then
compile
chsum1=$chsum2
fi
sleep 2
done
}
Works on my OS X as I do not have digest
.
On Linux, you can use md5sum
as a replacement for the md5
command.
回答2:
Here's an example of watching a folder for changes and running a the less compiler when one is updated. As a prereq you need npm
and these the module onchange. The node community has a whole bunch of different watch commands (like onchange) I'm not aware of any that are compiled self-contained binaries.
npm install less onchange -g
Then you can use something like:
onchange "./stylesheets/*.less" -- lessc main.less > main.css
I prefer a BASH command over the Grunt answer I gave a while back.
回答3:
METHOD 1:
#!/bin/sh
check() {
dir="$1"
chsum1=`digest -a md5 $dir | awk '{print $1}'`
chsum2=$chsum1
while [ $chsum1 -eq $chsum2 ]
do
sleep 10
chsum2=`digest -a md5 $dir | awk '{print $1}'`
done
eval $2
}
check $*
This script takes in two parameters [directory, command]. Every 10 seconds the script executes check()
to see it the folder has changed. If not it sleeps and the cycle repeats.
In the event that the folder has changed, it eval
s your command.
METHOD 2:
Use a cron to monitor the folder.
You'll have to install incron:
sudo apt-get install incron
And then you're script will look something like this:
#!/bin/bash
eval $1
(You won't need to specify the folder since it will be the job of the cron to monitor the specified directory)
A full, working example can be found here:
http://www.errr-online.com/index.php/2011/02/25/monitor-a-directory-or-file-for-changes-on-linux-using-inotify/
回答4:
I can’t believe nobody posted this yet.
First make sure inotify-tools
are installed.
Then use them like this:
logOfChanges="/tmp/changes.log.csv" # Set your file name here.
# Lock and load
inotifywait -mrcq $DIR > "$logOfChanges" &
IN_PID=$$
# Do your stuff here
...
# Kill and analyze
kill $IN_PID
cat "$logOfChanges" | while read entry; do
# Split your CSV, but beware that file names may contain spaces too.
# Just look up how to parse CSV with bash. :)
path=...
event=...
... # Other stuff like time stamps?
# Depending on the event…
case "$event" in
SOME_EVENT) myHandlingCode path ;;
...
*) myDefaultHandlingCode path ;;
done
Alternatively, using --format
instead of -c
on inotifywait
would be an idea.
Just man inotifywait
and man inotifywatch
for more infos.
回答5:
probably the fastest way of doing it.. (on 1G git repo, returns under 1sec.)
#!/bin/bash
watch() {
echo watching folder $1/ every $2 secs.
while [[ true ]]
do
files=`find $1 -type f -mtime -$2s`
if [[ $files == "" ]] ; then
echo "nothing changed"
else
echo changed, $files
fi
sleep $2
done
}
watch folder 3
回答6:
In Mac OS X, you can just control-click a folder, then click 'Folder Actions Setup'. This will allow you attach actions to a folder, i.e. scripts to run.
OS X comes with a number of prebuilt scripts, or you can create your own.
回答7:
Almost 3 years later and I'd recommend this grunt based solution.
- grunt
- grunt-contrib-watch
- grunt-shell
I created a working example here https://github.com/reggi/watch-execute.
Here's the Gruntfile.js
:
module.exports = function (grunt) {
grunt.initConfig({
shell: {
run_file:{
command: 'sh ./bash.sh',
options: {
stdout: true
}
}
},
watch: {
run_file: {
files: ["./watchme/*"],
tasks: ["shell:run_file"]
}
}
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-shell');
};
回答8:
I wrote a general utility called watchfile
for simplifying these kinds of operations.
It is less powerfull than inotifywatch
, but I prefer a simpler, less verbose utility.
For the desired task, you want to monitor if any files in current directory have been modified. To list all files recursively in the current directory:
find . -type f
To output the timestamp information of each of these files:
find . -type f -print0 | xargs -0 stat
Now, you can monitor this output with the watchfile
utility and execute a command CMD
when this information changes:
watchfile -s "find . -type f -print0 | xargs -0 stat" -e CMD
回答9:
Why not using AppleScript
http://www.tuaw.com/2009/03/26/applescript-exploring-the-power-of-folder-actions-part-iii/
on adding folder items to this_folder after receiving added_items
tell application "Finder"
...
回答10:
If you only need to check for files being created/deleted on top level (not checking subfolders) you might want to use the following.
It uses few ressources hence it can react quickly, I use it to check for a changed file.
#!/bin/bash
file="$1"
shift
tmp=$(mktemp)
trap 'rm "$tmp"' EXIT
while true; do
while [ ! "$tmp" -ot "$file" ]; do
sleep 0.5
done
eval "$@ &"
echo $! > "$tmp"
wait
done
回答11:
#!/bin/bash
# Author: Devonte
# NGINX WATCH DAEMON
# Place file in root of nginx folder /etc/nginx
# This will test your nginx config on any change and
# if there are no problems it will reload your configuration
# USAGE: sh nginx-watch.sh
dir=`dirname $0`
checksum_initial=`tar -cf - $dir | md5sum | awk '{print $1}'`
checksum_now=$checksum_initial
# Start nginx
nginx
while true
do
sleep 3
checksum_now=`tar -cf - $dir | md5sum | awk '{print $1}'`
if [ $checksum_initial != $checksum_now ]; then
echo "[ NGINX ] A configuration file changed. Reloading..."
nginx -t && nginx -s reload;
fi
checksum_initial=$checksum_now
done
回答12:
Here's a template to work with, it'll check every 120 seconds for changes in passed directory and notify on creation of directories,files,or names pipes. If you also want to run commands when something is removed then check my other answer on stackoverflow for additional looping examples.
#!/usr/bin/env bash
Var_dir="${1:-/tmp}"
Var_diff_sleep="${2:-120}"
Var_diff_opts="--suppress-common-lines"
Func_parse_diff(){
_added="$(grep -E '>' <<<"${@}")"
if [ "${#_added}" != "0" ]; then
mapfile -t _added_list <<<"${_added//> /}"
_let _index=0
until [ "${#_added_list[@]}" = "${_index}" ]; do
_path_to_check="${Var_dir}/${_added_list[${_index}]}"
if [ -f "${_path_to_check}" ]; then
echo "# File: ${_path_to_check}"
elif [ -d "${_path_to_check}" ]; then
echo "# Directory: ${_path_to_check}"
if [ -p "${_path_to_check}" ]; then
echo "# Pipe: ${_path_to_check}"
fi
let _index++
done
unset _index
fi
}
Func_watch_bulk_dir(){
_current_listing=""
while [ -d "${Var_dir}" ]; do
_new_listing="$(ls "${Var_dir}")"
_diff_listing="$(diff ${Var_dec_diff_opts} <(${Var_echo} "${_current_listing}") <(${Var_echo} "${_new_listing}"))"
if [ "${_diff_listing}" != "0" ]; then
Func_parse_diff "${_diff_listing}"
fi
_current_listing="${_new_listing}"
sleep ${Var_diff_sleep}
done
}
Hint if you replace the echo
lines above with eval <some command>
for each type of action monitored for you'll be all the closer to automation of actions. And if you wish to see what the above looks like when used within a script then check out the latest script version for the project I've been working on for automation of encryption and decryption via gpg and tar.