Bash脚本,监视文件夹,执行命令

52

我正在尝试创建一个带有两个参数的Bash脚本:

  • 一个目录
  • 一个命令

我想要监视目录参数以进行更改:当有更改时,脚本应该执行该命令。

我正在运行MacOS,而不是Linux;任何指针或外部资源都将非常有帮助,因为我发现这很难实现。实际上,我正在尝试模仿SASS的watch功能。

#!/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
13个回答

47

持续递归监视文件夹(md5),并在更改时执行命令:

daemon() {
    chsum1=""

    while [[ true ]]
    do
        chsum2=`find src/ -type f -exec md5 {} \;`
        if [[ $chsum1 != $chsum2 ]] ; then           
            if [ -n "$chsum1" ]; then
                compile
            fi
            chsum1=$chsum2
        fi
        sleep 2
    done
}

在我的OS X上可以工作,因为我没有digest

在Linux上,您可以使用md5sum代替md5命令。


4
好的,我建议用chsum1=$chsum2替换chsum1=...。否则,在compile过程中发生的更改将不会被注意到。 - Kleist
4
在 OS X 上有一个名为 "fswatch" 的工具,可以在这里找到: https://github.com/alandipert/fswatch。它是一个小型命令,使用 FSEvents API 完成相同的任务,但能节省 CPU 资源。 对于大型项目来说更好(不需要对所有内容进行 MD5 计算)。 - oshyshko
4
小建议:find src/ -type f -mtime -5s 明显更快,而且几乎不需要 CPU,因为它不会在每个文件上执行操作。它检查过去 5 秒是否有任何变化。 - Devrim
@devrim 我的“find”好像不喜欢 “-5s” 作为mtime参数。 它将接受int或float(天数)。 我正在使用Gnu find v4.4.2,这是Ubuntu 15.04上的当前版本。 是我的问题吗? - Jonathan Hartley
1
不要每次都对文件进行哈希,只需比较时间戳 ls --full-time $file | awk '{ print $7 }' 即可。我更喜欢这个实现方案 > https://dev59.com/xW855IYBdhLWcg3w-JMr#25869844 。 - qodeninja
显示剩余3条评论

17

我简直不敢相信还没有人发布这个。

首先确保已安装 inotify-tools

然后像这样使用它们:

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

作为替代,可以考虑在inotifywait上使用--format而不是-c

要了解更多信息,只需查看inotifywaitinotifywatch的手册即可。


2
刚试图在 Mac 上安装 inotify-tools,但失败了。如果您感兴趣,我已经发布了 github 问题 - ThomasReggi
我在你的Github问题中发布了一个解决方案。这只是一个autoconf版本不匹配的问题。 :) - anon
4
我注意到" inotify-tools"需要支持"inotify"的Linux内核。除非你知道关于Mac OS X和"inotify"的一些其他人不知道的事情,否则没有直接的"inotify"支持(但"FSEvents" API - 文件系统事件 - 将接近所需的功能,即使接口不同)。 - Jonathan Leffler
2
@JonathanLeffler:fswatch 使用 FSEvents,似乎是一个不错的全功能选择:https://dev59.com/hXI_5IYBdhLWcg3wHvU5 - anon

15

这是一个监听文件夹变化并在更新时运行less编译器的示例。作为前提,您需要安装npmonchange模块。Node社区有很多不同的监视命令(如onchange),但我不知道是否有已编译的自包含二进制文件。

npm install less onchange -g

然后您可以使用类似以下的内容:

onchange "./stylesheets/*.less" -- lessc main.less > main.css

我更喜欢之前提到的一个BASH命令,而不是我之前给出的Grunt答案


1
因为你的问题直接涉及bash,我希望答案更加关注bash而不是npm。我强烈建议选择得票更高的bash答案。 - qodeninja

12

方法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 $*
该脚本接受两个参数 [directory, command]。每 10 秒钟,脚本执行 check() 来检查文件夹是否发生了更改。如果没有更改,它会休眠并且循环重复。
如果文件夹发生了更改,它会 eval 执行您的指令。 方法 2:
使用 cron 监视该文件夹。
您需要安装incron:
 sudo apt-get install incron

然后你的脚本将会长成这个样子:

#!/bin/bash
eval $1

由于cron将监视指定目录,因此您不需要指定文件夹。

这里可以找到一个完整的可工作示例:

http://www.errr-online.com/index.php/2011/02/25/monitor-a-directory-or-file-for-changes-on-linux-using-inotify/


已编辑以满足您的需求。祝您使用愉快。 - Swift
3
жӮЁеҸҜд»ҘдҪҝз”Ёmd5sumпјҲжҲ–жӣҙеҸҜйқ зҡ„sha1sumпјүжқҘе®һзҺ°жӯӨзӣ®зҡ„гҖӮ - Adam Liss
1
这里有一种新的方法,使用cron。 - Swift

7

可能是最快的方法..(在1G Git仓库上,返回时间少于1秒。)

#!/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

1
谢谢您的评论。未来读者:当我发布这个答案时,除了grunt之外没有其他选择 - 现在我看到有一些以上使用相同方法但更好的答案;请使用他们的解决方案。 - Devrim
2
这很简短而且很流畅,因为您不必安装其他依赖项。但是它无法捕获删除操作,只能添加/更新。 - bigtunacan

3
在Mac OS X中,您只需控制单击文件夹,然后单击“文件夹操作设置”。这将允许您将操作附加到文件夹,例如运行脚本。OS X带有许多预先构建的脚本,或者您可以创建自己的脚本。

2
文件夹操作触发缓慢。 - raine

1
#!/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

我喜欢使用 tarmd5sum 来确定文件夹是否发生了变化。为了在 macOS 上使其正常工作,我必须进行一些调整:checksum_initial=\tar -cf - $dir | md5`` - Richard

1

将近三年后,我建议使用基于Grunt的解决方案。

我在这里创建了一个可行的示例https://github.com/reggi/watch-execute

这是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');
};

1
我编写了一个名为watchfile的通用实用程序,用于简化这些类型的操作。
它不如inotifywatch强大,但我更喜欢一个更简单、更简洁的实用程序。
对于所需的任务,您要监视当前目录中是否有任何文件被修改。要递归列出当前目录中所有文件,请执行以下操作:
find . -type f

要输出这些文件的时间戳信息:
find . -type f -print0 | xargs -0 stat

现在,您可以使用watchfile实用程序监视此输出,并在此信息更改时执行命令CMD:
watchfile -s "find . -type f -print0 | xargs -0 stat" -e CMD

0
如果您只需要检查顶层文件(不检查子文件夹)是否被创建/删除,您可能想使用以下方法。
它使用的资源很少,因此可以快速响应,我用它来检查更改的文件。
#!/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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接