我正在努力想出一种很好的方法来检测我想要观看的文件夹中是否有任何写入活动。
基本上,我想拥有的是这样的:
#!/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上使用自制软件安装的东西都可以,但越简单越好(所以我理解发生的事情)。
答案 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
CURRENT_TIME=date +%s
(带有反引号的旧语法 - CURRENT_TIME=$(date +%s)
- 也有效,但有缺点)。 CURRENT_TIME=`date +%s`
[ CURRENT_TIME - LAST_CHECKED > TIMEOUT_TIME ]
>
和bash的[ ... ]
条件 lexical 比较,变量名称必须为[[ ... ]]
- 前缀;您必须使用算术条件语法:$
(( CURRENT_TIME - LAST_CHECKED > TIMEOUT_TIME ))
fswatch -r ${1} | refreshTimer
将不会被调用,因为您没有尝试逐行读取 。 refreshTimer
中的变量也不会被保留,因为refreshTimer
每次都在新的子shell 中运行,因为使用的是管道(refreshTimer
)。在|
中,这个问题经常通过process substitution(bash
)提供输入来解决,您可以在上面的代码中看到这些输入。