基于 Bash 的热重载实现

Posted

技术标签:

【中文标题】基于 Bash 的热重载实现【英文标题】:Bash-based Hot Reload Implementation 【发布时间】:2017-06-22 08:15:21 【问题描述】:

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 循环实现,但我很难跟踪文件更改(最好以较少资源占用的间隔进行跟踪)。任何帮助将不胜感激!

【问题讨论】:

你可以访问inotify吗? 我猜是的 - 我现在无法访问虚拟机,但它是基于 CentOS 构建的,所以我猜是的... 你能用which inotifywait快速确认一下吗?看看它是否存在。使用它很简单,或者像github.com/rvoicilas/inotify-tools/wiki一样下载 which inotifywait: /usr/bin/which: no inotifywait in (...) 当我尝试运行 which inotify... 时,结果也相同...我有 sudo 访问权限和如果这有助于解决热重载问题,可以运行yum install :) 【参考方案1】:

使用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.

【讨论】:

Mm.. 我的盒子失去了传出连接,所以我正在将.tar.gz 转移到机器上,但是我对autoreconfautomake 不是很熟悉(不确定命令行选项)。哪些命令可以帮助我安装 inotify-tools @SunnyPun: 只需按照页面中提到的./configure --prefix=/usr &amp;&amp; make &amp;&amp; su -c 'make install',在解压缩存档后在文件夹中执行 哦,我太傻了……我在运行inotifywait时遇到了这个错误:inotifywait: error while loading shared libraries: libinotifytools.so.0: cannot open shared object file: No such file or directory。除了ldconfig: /usr/lib/libinotifytools.so.0 is not a symbolic link,安装似乎进展顺利。 @SunnyPun:将另一个选项添加到./configure./configure --prefix=/usr --libdir=/lib64 &amp;&amp; make &amp;&amp; su -c 'make install' 感谢您的耐心等待.. 现在我正在运行此 inotifywait -q -m -e close_write gg.txt | &gt; while read -r filename event; do &gt; ls &gt; done 并在另一个 bash 上的同一文件夹中执行 vi gg.txt(并使用 :x 保存文件)。这给了我没有输出...我该怎么办?【参考方案2】:

我最终想出了这个功能给我的~/.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 ]] &amp;&amp; a=$b &amp;&amp; eval $script; 行是为了避免将自己与嵌套 if 混淆 - 它是执行 a=$b; eval $script if $a != $b 的“简短形式”。

希望这可以帮助其他人寻找答案。 :)

【讨论】:

以上是关于基于 Bash 的热重载实现的主要内容,如果未能解决你的问题,请参考以下文章

如何检测 Flutter App 代码中的热重载?

不使用 Blazor WebAssembly 托管的 CSS 的热重载

关于在 Windows 环境下开发 react native 的热重载问题

如何使用 Webpack 的热重载更新 Apollo 服务器上的模式?

通过 devtools 控制台为 Angular 应用程序断开特定选项卡的热重载

Flutter“不能热加载(hot reload),热重载按钮灰色且无法点击”的解决方案