尝试使用脚本来监视文件并每 60 秒比较文件的 md5sum,如果它已更改,则在屏幕上打印警告。
不知道该怎么做。这就是我所拥有的,但我认为我有点偏离了
#/bin/bash
watch=$@
if [ -z "$watch" ]
then
echo "No file specified, aborting"
exit
else
echo "watching : $watch"
fi
while [ 1 ]; do
watch -n 60 -d md5sum $watch
done
我也这样做了,这似乎有效(有点),它确实告诉我文件是否已更改,但它不使用 watch 命令,有没有办法通过 watch 来做到这一点?
#/bin/bash
watch=$@
if [ -z "$watch" ]
then
echo "No file specified, aborting"
exit
else
echo "watching : $watch"
fi
checksum1="empty"
while [ 1 ]; do
checksum2=$(md5sum $watch | md5sum | cut -d ' ' -f1)
if [ "$checksum2" != "$checksum1" ];
then
echo "Warning : $watch has been changed"
#mail -s "$watch has been changed" "[email protected]"
echo -e "\a"
fi
checksum1="$checksum2"
sleep 60
done
答案1
你的情况要好得多inotify,它就是为了这种目的而制作的:文件监控。
编辑:我将在这里总结链接问题的答复。
每当您需要监视文件或目录时,inotify 都是适合该工作的工具。您可以告诉inotifywait您要监视哪些事件:文件访问、更改、打开、关闭、删除...(man inotifywait
更多详细信息请参阅)。
第一种方法是像这样的循环:
while inotifywait -e close_write myfile.py; do ./myfile.py; done
主要缺点是您可能会错过活动。更有效的版本是:
inotifywait -mq --format '%e' /path/to/file |
while IFS= read -r events; do
/path/to/script "$events"
done
后一个版本不会错过任何一个事件。管道确保事件排队。如果循环没有及时挑选它们,它们就会堆积起来,但不会错过任何一个。
编辑:如果您正在跟踪文本文件,我还推荐 git,因为它使用强大的哈希来跟踪文件。下面是一些关于主循环的 bash 伪代码inotify
:
git status file # Tells whether <file> was modified
if file was modified; then
git commit -- file # Add <file> to the repository
keep only the last two versions of <file>
print the warning message
fi
您可以使用它作为基础。也许你必须解析git
输出。我还没有充分地使用它来告诉你具体如何,但我有一种预感它可以做到这一点;-)。
答案2
我讨厌这个答案,但在玩了大约 15 分钟后,我认为没有办法用命令来实现它watch
(尽管是其他人,请证明我错了)
问题在于watch
它本身在自己的循环中运行,并且不会中断向 shell 提供数据,它只提供自己的回显。
由于watch
run 的方式,在任何类型的if
或while
检查语句中使用它都不会让 shell 有机会评估所做的任何更改,因为它永远不会返回其结果。它只是保留它们,直到您手动退出它的循环。
按照第二个示例中的方式运行您自己的循环并检查 md5sum 是完成您想要做的事情的最佳方法。
举个例子,问题
echo `watch -d md5sum testfile.txt`
回声永远不会消失。如果你能找到一种方法让回声到达终端,那么那是让你的脚本运行的答案。
答案3
尝试这个粗略的脚本,它遵循您提供文件参数并使用 md5sum 的想法。只要 md5sum 不同而发生更改,它就会显示带有时间戳的行,保存日志文件,并在您按 ctrl-c 时停止。内容watch_and_notify.sh
:
#!/bin/bash
logf="$1.log"
interval=2
first_run=
# temp files, current and last md5s for diff to compare
lm1="$(mktemp /tmp/lm1.$$.XXXX)"
lm2="$(mktemp /tmp/lm2.$$.XXXX)"
if [ -z "$1" ]; then
echo "No file specified, aborting" >&2
exit 1
fi
echo "Watching at ${interval}s intervals: $1"
# loop forever until cancel this script
while true; do
md5sum "$1" > $lm1
# otherwise in the first iteration,
# lm2 does not yet exist, so diff
# will always unintentionally report
# a difference when comparing existing
# file with nonexisting file
if [ -z "$first_run" ]; then
cp -a $lm1 $lm2
first_run=1
fi
# test ! to invert usual exit code
if ! diff $lm2 $lm1; then
echo -e "$(date +"%F %R")\tChange detected:\t$1" | tee -a "$logf"
fi
# rotate
mv $lm1 $lm2
sleep $interval
done
# when you ctrl-c it should garbage cleanup
trap "rm $lm1 $lm2; exit 1" SIGINT
例子
启动一个名为的空文本文件a.txt
$ touch a.txt
像这样运行脚本,然后看到:
$ ./watch_and_notify.sh a.txt
Watching at 2s intervals: a.txt
在第二个终端上,您测试进行更改,例如
$ echo addition >> a.txt
在运行脚本的第一个终端上,您将看到更新:
Watching at 2s intervals: a.txt
1c1
< d41d8cd98f00b204e9800998ecf8427e a.txt
---
> 9913e6909c108b5c32c69280474b2b2a a.txt
2015-09-29 15:56 Change detected: a.txt
在第二个终端上,您再次引入另一个更改:
$ echo anotherchange >> a.txt
然后在运行脚本的第一个终端上,输出再次更新:
Watching at 2s intervals: a.txt
1c1
< d41d8cd98f00b204e9800998ecf8427e a.txt
---
> 9913e6909c108b5c32c69280474b2b2a a.txt
2015-09-29 15:56 Change detected: a.txt
1c1
< 9913e6909c108b5c32c69280474b2b2a a.txt
---
> 5c1c20a75b9982128f8300a7940f9ce0 a.txt
2015-09-29 16:06 Change detected: a.txt
你退出了ctrl-c
。并将返回到命令提示符。您列出内容并看到有一条日志:
$ ls -lh
total 12K
-rw-r--r-- 1 meme meme 22 Sep 29 16:06 a.txt
-rw-r--r-- 1 meme meme 80 Sep 29 16:06 a.txt.log
-rwxrwxrwx 1 meme meme 775 Sep 29 15:26 watch_and_notify.sh
查看日志,您会看到记录更改时刻的相同时间戳条目:
$ cat a.txt.log
2015-09-29 15:56 Change detected: a.txt
2015-09-29 16:06 Change detected: a.txt
代码解释
大部分整体流程可以从脚本中的注释中理解,但基本上它会在文件上重复运行 md5sum 命令,同时保存结果并旋转它,以便程序将diff
当前结果与上一次迭代的结果进行比较(如果有)然后执行报告操作。在这种情况下,这些操作将带有时间戳输出到屏幕以及附加到日志。用户使用 ctrl-c 停止脚本,并留下检测到差异时带时间戳的时刻的日志。
在脚本内
lm1="$(mktemp /tmp/lm1.$$.XXXX)"
lm2="$(mktemp /tmp/lm2.$$.XXXX)"
- lm1 和 lm2 是用于存储 md5sum 输出以进行比较的临时文件,仅在运行脚本时生成
- 关键方法正如你所说,从字面上比较 md5sums,所以要做到这一点,我们首先定义一个临时的地方来存储它们
mktemp
帮助创建唯一的临时文件名。$$
是当前进程ID,加入一些随机性XXXXX
告诉用随机的字母数字字符mktemp
替换每个字符X
因此,在运行时,我们可以检查并看到 /tmp 确实至少包含一个以我们定义的模式命名的文件:
$ ls -lh /tmp/lm*
-rw-r--r-- 1 meme meme 40 Sep 29 15:08 /tmp/lm2.8248.xGJTl
接下来我们有 while 循环:
# loop forever until cancel this script
while true; do
...
done
大部分代码都夹在一个大while <command>; do ... done
循环中。由于命令/条件true
始终如此,因此该代码将无限期地运行,直到我们ctrl-c
停止它为止。
每个循环迭代首先生成当前迭代的 md5sum 结果,并保存它:
md5sum "$1" > $lm1
$1
表示第一个位置参数。在这种情况下,当运行时watch_and_notify.sh a.txt
,$1
将是a.txt
> $lm1
将输出写入之前定义的临时文件
第一次运行时,不会有先前的迭代。然而,该命令的排列方式diff
必须将先前 md5sum 结果的变化$lm2
与进行比较$lm1
,在第一次运行时,它总是会无意中显示出差异,因此在第一次运行时需要有一个特殊的条件操作。为了能够识别第一次运行,我们创建一个初始空变量,在while
循环之前定义:
first_run=
然后,在循环中我们对此进行测试:
if [ -z "$first_run" ]; then
cp -a $lm1 $lm2
first_run=1
fi
-z
测试零值。如果是第一次迭代,$first_run 始终为空,因此继续该then
部分- 在该
then
部分中,我们通过复制 来“伪造”“先前的迭代”$lm1
,因此稍后diff
,对于第一次迭代,将比较两个相同的文件,并且不会报告差异。 first_run=1
这样下一次迭代if [ -z "$first_run" ]; then
,$first_run
将不再是零值,因此该then
部分将不会被触发,从而确保仅在第一次迭代时执行此操作
接下来我们有实际diff
条件,将先前迭代的 md5sum 结果(保存到变量 中引用的文件)$lm2
与当前迭代的 md5sum 结果(保存到变量 中引用的文件)进行比较$lm1
if ! diff $lm2 $lm1; then
- 我们依赖于对
diff
命令的退出代码做出反应 diff file1 file2
当它们相同时,通常会导致退出代码 0。你可以在运行时测试这个$ diff a.txt a.txt; echo $?
,你会看到一个零。当不同的时候eg$ diff a.txt b.txt; echo $?
只要b.txt
不同就会有一个结果1
- 但
0
bash 认为其含义是true
和1
意思false
- 所以我们不能这样做,
if diff $lm2 $lm1; then
因为diff
当文件相同时,会给出退出代码 0,解释为true
,并触发该then
部分 - 我们想要相反的行为,如果相同则不执行任何操作,如果不相同则执行某些操作
!
帮助反转输出
所以我们可以测试做出改变,
当发生变化时,采取的操作是:
echo -e "$(date +"%F %R")\tChange detected:\t$1" | tee -a "$logf"
echo
with-e
呈现\t
为选项卡date +"%F %R"
以给定格式呈现当前时间戳,例如 2015-09-29 15:56|
将输出通过管道传输到tee
程序tee
允许我们查看有关发生更改的输出消息,以及保存输出-a
设置保存方式为append,否则每次有结果都会覆盖之前的结果
我们几乎完成了当前迭代的任务。完成diff
比较和操作后,我们这样做是为下一次迭代做准备:
mv $lm1 $lm2
mv
$lm1
将保存在, 处的当前迭代的 md5sum 结果移动/重命名为$lm2
。- 所以
diff $lm2 $lm1
它的下一次迭代确实会比较上一次迭代的 md5sum
最后循环的最后一行是sleep子句sleep $interval
sleep
导致延迟(以秒为单位),由$interval
变量给出$interval
在文件开头设置的变量是2
所以每次迭代将持续 2 秒
完毕
最后有一个
done
关闭while ...;do ... done
循环