观察文件并比较校验和

观察文件并比较校验和

尝试使用脚本来监视文件并每 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 提供数据,它只提供自己的回显。

由于watchrun 的方式,在任何类型的ifwhile检查语句中使用它都不会让 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
  • 0bash 认为其含义是true1意思false
  • 所以我们不能这样做,if diff $lm2 $lm1; then因为diff当文件相同时,会给出退出代码 0,解释为true,并触发该then部分
  • 我们想要相反的行为,如果相同则不执行任何操作,如果不相同则执行某些操作
  • !帮助反转输出

所以我们可以测试做出改变,

当发生变化时,采取的操作是:

    echo -e "$(date +"%F %R")\tChange detected:\t$1" | tee -a "$logf"
  • echowith-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循环

相关内容