当文件夹中没有写入活动时要通知的任何脚本

时间:2015-05-21 12:33:37

标签: macos bash terminal fswatch

我正在努力想出一种很好的方法来检测我想要观看的文件夹中是否有任何写入活动。

基本上,我想拥有的是这样的:

#!/bin/sh

# colored red text (error)
function errorMsg () {
  echo '\033[0;31m'"$1"'\033[0m'
}

# check for folder to monitor argument
if [ "$#" -ne 1 ]
then
  errorMsg "add experiment (folder) to monitor for activity!"
exit
fi

# time out time, 3 minutes
TIMEOUT_TIME=180
LAST_CHECKED=0

function refreshTimer () {
  # when called, check seconds since epoch
  CURRENT_TIME=date +%s

  if [ CURRENT_TIME - LAST_CHECKED > TIMEOUT_TIME ]
  then
    echo "file write activity halted!" | mail -s "trouble!" "user@provider.ext"
  fi

  LAST_CHECKED=date +%s
}

# set last checked to now.
LAST_CHECKED=date +%s
# start monitoring for file changes, and update timer when new files are being written.
fswatch -r ${1} | refreshTimer

但是我认为需要各种各样的bash魔法,因为fswatch是一个后台任务并且管道输出会创建一个子shell。 我还需要一些计时器逻辑...... 我正在考虑类似于setTimeout的东西,其中时间参数在有活动的时候不断被添加,但我不知道如何在一个脚本中编写它。

Bash,Python,Ruby,任何可以在OSX上使用自制软件安装的东西都可以,但越简单越好(所以我理解发生的事情)。

1 个答案:

答案 0 :(得分:1)

请尝试以下操作 - 请注意,它需要bash

#!/usr/bin/env bash

# colored red text (error)
function errorMsg () {
  printf '\033[0;31m%s\033[0m\n' "$*" >&2
}

# check for folder to monitor argument
if [[ $# -ne 1 ]]
then
  errorMsg "add experiment (folder) to monitor for activity!"
  exit 2
fi

# time-out: 3 minutes
TIMEOUT_TIME=180

# Read fswatch output as long as it is
# within the timeout value; every change reported
# resets the timer.
while IFS= read -t $TIMEOUT_TIME -d '' -r file; do 
  echo "changed: [$file]"
done < <(fswatch -r -0 "${1}")

# Getting here means that read timed out.
echo "file write activity halted!" | mail -s "trouble!" "user@provider.ext"
  • fswatch 无限期地向stdout输出行,必须逐行读取以对新输出执行及时操作。
  • fswatch -0终止具有NUL(零字节)的行,read然后通过将行分隔符(分隔符)设置为空字符串(-d '')来读取一行。
  • < <(fswatch -r -0 "${1}")通过stdin <while循环提供输入,其中read一次消耗stdin输入一条NUL终止的行。
    • <(fswatch -r -0 "${1}")是一个process substitution,它从fswatch -r -0 "${1}"(监视文件夹{{)生成的输出中形成一个“ad-hoc文件”(技术上,一个FIFO或命名文件描述符)。 1}}用于文件更改的子树(${1}),以及每个以NUL(-r)终止的报告。
    • 由于-0命令无限期运行,“ad-hoc文件”将继续提供输入,但通常只是间歇性地提供,具体取决于文件系统活动。
  • 每当fswatch命令在超时时段(read)内收到新行时,它会成功终止 (退出代码0),从而导致循环体执行,然后-t $TIMEOUT_TIME再次调用
    • 因此,只要收到一行,就会有效地重置超时时间,因为新的read调用会在超时时间内重新开始。
    • 相反,如果超时期限在收到另一行之前到期,read终止失败 - 使用非零退出代码表明失败< / em>,这会导致read循环终止。
  • 因此,仅当while命令超时时才会到达循环后的代码。

至于原始代码

注意:在shellecheck.net

的帮助下,可以检测到所讨论的一些问题
  • read
    • echo '\033[0;31m'"$1"'\033[0m'是解释转义序列的最佳选择,因为printf的行为因shell和平台而异;例如,使用echo运行脚本将解释它们(除非您还添加bash选项)。
  • -e
    • function refreshTimer ()语法是非标准的(不符合POSIX),所以你不应该使用function shebang行(这是chepner在评论中的含义)。在OSX上,你可以逃脱它,因为sh充当bash大多数 bashisms在以sh运行时仍然可用,但它不会在其他系统上工作。如果您知道自己将使用sh,那么最好使用bash shebang line。
  • bash
    • 您不能通过简单地将命令放在分配的RHS上来将命令的输出分配给变量;相反,你需要一个command substitution;在手头的情况下:CURRENT_TIME=date +%s(带有反引号的旧语法 - CURRENT_TIME=$(date +%s) - 也有效,但有缺点)。
  • CURRENT_TIME=`date +%s`

      {li> [ CURRENT_TIME - LAST_CHECKED > TIMEOUT_TIME ] >和bash的[ ... ]条件 lexical 比较,变量名称必须为[[ ... ]] - 前缀;您必须使用算术条件语法:$
    • 顺便说一下,在shell编程中它更好better not to use all-uppercase variable names
  • (( CURRENT_TIME - LAST_CHECKED > TIMEOUT_TIME ))

      在管道填满(您无法预测的时间)之前,
    • fswatch -r ${1} | refreshTimer将不会被调用,因为您没有尝试逐行读取
    • 即使你解决了这个问题,refreshTimer中的变量也不会被保留,因为refreshTimer每次都在新的子shell 中运行,因为使用的是管道(refreshTimer)。在|中,这个问题经常通过process substitutionbash)提供输入来解决,您可以在上面的代码中看到这些输入。