基于 Bash 的热重载实现



tl;dr 版本问题:如何制作 bash 脚本/命令来监听对文件的更改,然后输出特定 Bash 命令的结果?

长版真实场景:我正在重构一个Perl模块(my_module.pm),我有一个与该模块(my_module.t)关联的测试文件。我想将控制台放在一个屏幕上,每当我保存 pm 文件(在另一个屏幕上使用编辑器)时,控制台都会运行prove -v my_module.t

背景:我拥有当前目录中的所有权限,如果需要,我可以提升到sudo。我不介意实现是否类似于setInterval,因为它仅用于开发目的。只要我有办法clearInterval并且脚本在文件不更改时不会产生永无止境的输出,那么它就很好:)

示例场景: 假设 bash 脚本名为hot,并且每当给定的文件更改时ls -l source.txt运行。 所以当我运行hot source.txt时,脚本可能会也可能不会运行一次ls ....。然后当我修改source.txt时,运行hot的控制台将再次运行ls,我应该看到source.txt的新文件大小(以及其他信息)。 如果我运行hot something.txt,当source.txt被修改时,它不应该运行ls。即使source.txt没有被修改,每当我修改something.txt时,脚本都应该触发ls

我想这可以通过while循环来实现,但我很难跟踪文件更改(最好以更少的资源密集的间隔进行跟踪)。任何帮助将不胜感激!

使用inotifywait监视文件的更改事件并对其修改运行测试。

inotifywait -q -m -e close_write my_module.pm |
while read -r filename event; do
prove -v my_module.t
done

标志的用法如下。在您的案例中,事件-e标志是close_write这意味着该文件在最近打开进行写入后已关闭。

-q, --quiet
If specified once, the program will be less verbose. Specifically, it will not state when 
it has completed establishing all inotify watches. If specified twice, the 
program will output nothing at all, except in the case of fatal errors.
-m, --monitor
Instead of exiting after receiving a single event, execute indefinitely. The default 
behaviour is to exit after the first event occurs.
-e <event>, --event <event>
Listen for specific event(s) only.
close_write
A watched file or a file within a watched directory was closed, after being opened in 
writeable mode. This does not necessarily imply the file was written to.

我最终想出了这个函数到我的~/.bashrc

function hot {
if (( $# < 2 )); then
echo 'USAGE: hot <command> <file1> [<file2> ... <fileN>]'
echo '<command> will be run once when any of the files listed is changed (i.e. ls -l <file> has its output changed)'
else
script=$1
shift
a='';
while true; do
b=`ls -l $*`
[[ $a != $b ]] && a=$b && eval $script;
sleep .5;
done
fi
}

所以我可以做hot 'prove my_module.t' my_module.pm,如示例中所述,我也可以做hot 'ls -l source.txt' source.txt

实际上,我希望在文件或测试文件更改后运行测试。 因此,我会做hot 'prove my_module.t' my_module.pm my_module.t.

[[ $a != $b ]] && a=$b && eval $script;行是为了避免将自己与嵌套if混淆 - 这是执行a=$b; eval $scriptif$a != $b的"简短形式"。

希望这有助于其他人寻找答案。 :)

最新更新