Bash 基于热重载的实现
Bash-based Hot Reload Implementation
tl;dr 版本
问题:如何制作 bash 脚本/命令来监听文件的更改,然后输出特定 Bash 命令的结果?
长版
真实场景:我正在重构一个 Perl 模块(my_module.pm)并且我有一个与该模块关联的测试文件(my_module.t).我想将控制台放在一个屏幕上,每当我保存 pm 文件(在另一个屏幕上使用编辑器)时,控制台都会 运行 prove -v my_module.t
.
背景:我拥有当前目录的所有权限,如果需要我可以提升到sudo
。我不介意实现是否类似于 setInterval
,因为它仅用于开发目的。只要我有办法 clearInterval
并且脚本不会在文件未更改时产生永无止境的输出,那就很好 :)
示例场景:
假设 bash 脚本被命名为 hot
并且它 运行s ls -l source.txt
只要给定的文件被更改。
因此,当我 运行 hot source.txt
时,脚本可能会或可能不会 运行 ls ....
一次。然后当我修改 source.txt
时,控制台 运行ning 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=
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;
是为了避免将自己与 nested-if 混淆 - 这是 "short form" 做 a=$b; eval $script
if $a != $b
.
希望这可以帮助其他人寻找答案。 :)
tl;dr 版本 问题:如何制作 bash 脚本/命令来监听文件的更改,然后输出特定 Bash 命令的结果?
长版
真实场景:我正在重构一个 Perl 模块(my_module.pm)并且我有一个与该模块关联的测试文件(my_module.t).我想将控制台放在一个屏幕上,每当我保存 pm 文件(在另一个屏幕上使用编辑器)时,控制台都会 运行 prove -v my_module.t
.
背景:我拥有当前目录的所有权限,如果需要我可以提升到sudo
。我不介意实现是否类似于 setInterval
,因为它仅用于开发目的。只要我有办法 clearInterval
并且脚本不会在文件未更改时产生永无止境的输出,那就很好 :)
示例场景:
假设 bash 脚本被命名为 hot
并且它 运行s ls -l source.txt
只要给定的文件被更改。
因此,当我 运行 hot source.txt
时,脚本可能会或可能不会 运行 ls ....
一次。然后当我修改 source.txt
时,控制台 运行ning 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=
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;
是为了避免将自己与 nested-if 混淆 - 这是 "short form" 做 a=$b; eval $script
if $a != $b
.
希望这可以帮助其他人寻找答案。 :)