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

问题描述 投票:0回答:14

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

  • 目录
  • 命令

我想观察目录参数的更改:当某些内容发生更改时,脚本应该执行该命令。

我运行的是 MacOS,而不是 Linux;任何指针或外部资源都会有很大帮助,因为我发现这很难实现。真的,OI 正在尝试模仿 SASS 的手表功能。

#!/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
macos bash shell
14个回答
48
投票

连续递归监视文件夹 (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
命令。


18
投票

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

首先确保

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

或者,在

--format
上使用
-c
代替
inotifywait
也是一个主意。

只需

man inotifywait
man inotifywatch
了解更多信息。


15
投票

这是一个监视文件夹更改并在更新时运行 less 编译器的示例。作为先决条件,您需要

npm
和这些模块 onchange。节点社区有一大堆不同的监视命令(例如 onchange),我不知道有任何已编译的独立二进制文件。

npm install less onchange -g

然后你可以使用类似的东西:

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

我更喜欢 BASH 命令,而不是我不久前给出的 Grunt 答案。


12
投票

方法一:

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

该脚本接受两个参数[目录、命令]。脚本每 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/


6
投票
可能是最快的方法..(在 1G git repo 上,返回时间不到 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
    

3
投票
在 Mac OS X 中,您只需按住 Control 键单击文件夹,然后单击“文件夹操作设置”。这将允许您将操作附加到文件夹,即要运行的脚本。

OS X 附带了许多预构建的脚本,或者您也可以创建自己的脚本。


1
投票
差不多 3 年后,我推荐这个基于 grunt 的解决方案。

  • 咕噜声
  • grunt-contrib-watch
  • grunt-shell
我在这里创建了一个工作示例

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
    

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
    

0
投票
为什么不使用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" ...
    

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
    

0
投票
这是一个可以使用的模板,它将每 120 秒检查一次传递的目录中的更改,并通知创建目录、文件或名称管道。如果您还想在删除某些内容时运行命令,请查看我在

stackoverflow 上的其他答案以获取其他循环示例。

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

提示,如果您将上面的

echo

 行替换为 
eval <some command>
,您将更接近于监控的每种类型的操作自动化。如果您希望了解上面在脚本中使用时的样子,请查看我一直致力于通过 gpg 和 tar 进行加密和解密自动化的项目的最新
脚本版本


0
投票

Radek 的答案“有点”对我有用(OSx),但它减慢了我的终端速度。我对其进行了一些修改,以下是我认为对我有用的内容:

#!/bin/bash daemon() { chsum1="" targetFolder=path-to-target-folder while [[ true ]] do chsum2=`find ${targetFolder} -type f | xargs stat -f "%m" | md5` if [[ $chsum1 != $chsum2 ]] ; then # echo the date to indicate we are updating date ./do-something.sh # tracks the check-sum chsum1=$chsum2 fi # sleep for 2 secs sleep 2 done } daemon
肉在:

chsum2=`find ${targetFolder} -type f | xargs stat -f "%m" | md5`
这意味着

find all files in ${targetFolder}, pipe that into stat -f "%m" 1-by-1 and then pipe that into md5

stat -f "%m" [filepath]
 为您提供最后修改的时间戳。

帮助我改进它。谢谢!


0
投票
您可以使用此命令获取目录的完整状态。

find src -name "*" | xargs stat -f "%Sa %N" | sort
示例:

PREVIOUS_STATE="" while true; do CURRENT_STATE=`find src -name "*" | xargs stat -f "%Sa %N" | sort` if [[ $CURRENT_STATE != $PREVIOUS_STATE ]]; then # do something here PREVIOUS_STATE=$CURRENT_STATE fi sleep 0.2 done
    
© www.soinside.com 2019 - 2024. All rights reserved.