在不使用子 shell 的情况下检测当前脚本是否已更改? (并启动新脚本)

Detect if current script has been changed WITHOUT using subshell? (and start the new script)

是否可以在不使用子 shell 命令的情况下使 运行 脚本查看它是否已经 changed/updated?

如果已经更新,则启动新脚本并杀死旧脚本。

之前我为它使用了一个单独的文件,所以当我创建文件时,脚本会检测到它。但是,如果您要 运行 多个脚本实例,这可能会非常混乱

if [[ -f /mnt/g/update.tt ]]; then script.sh 2 && kill $$ ;fi

这个函数将被放置在一个大约需要 0.8 秒的循环中,这就是为什么没有子 shell 是重要的。

也许:

age_of_script=$(( $(date +%s) - $(stat -c %Y "[=10=]") ))
running_time=$SECONDS

if (( running_time > age_of_script )); then
    # script has been updated since I started running
    exec "[=10=]" 2
fi

正如that other guy评论的那样,使用exec替换当前进程。


使用 bash 4.3+,您可以使用:

bash_root=${BASH%/bin/bash}
if [[ -d "$bash_root/lib/bash" ]]; then
  enable -f "$bash_root/lib/bash/finfo" finfo
  file_age() { echo $(( $(printf '%(%s)T' -1) - $(finfo -m "") )); }
else
  file_age() { echo $(( $(printf '%(%s)T' -1) - $(stat -c %Y "") )); }
fi
age_of_script=$(file_age "[=11=]")

仍然使用子 shell 进行命令替换,但是如果您的 bash 构建具有可加载模块,您可能不需要使用任何外部工具


为了后代,stat 与可加载 finfo

的快速基准
$ file_age() { echo $(( $(printf '%(%s)T' -1) - $(stat -c %Y "") )); }
$ time for ((i=0; i<1000; i++)); do x=$(file_age /etc/hosts); done

real    0m14.750s
user    0m2.288s
sys     0m4.139s
$ file_age() { echo $(( $(printf '%(%s)T' -1) - $(finfo -m "") )); }
$ time for ((i=0; i<1000; i++)); do x=$(file_age /etc/hosts); done

real    0m7.162s
user    0m1.148s
sys     0m2.085s

您可以创建一个参考文件,然后继续检查当前脚本是否比该参考文件更新:

#!/bin/bash
reference=$(mktemp)
thisscript="${BASH_SOURCE[0]}"
trap 'rm "$reference"' EXIT

printf '\nStarting\n'
while true
do
  if [[ "$thisscript" -nt "$reference" ]] 
  then
    rm "$reference"
    exec "$thisscript"
  fi
  printf "beep boop "
  read -t 0.8
done

自动更新脚本的最好和最简单的方法是将脚本修改标记与您在脚本开头创建的临时文件进行比较。

  scriptupdate=$(mktemp)

只要确保你只 运行 mktemp 一次,并且在任何循环之外,否则脚本会一遍又一遍地更新控制文件,使其成为比脚本更新..

那么你只需要将脚本文件与临时文件进行比较,看是否是never,然后重启脚本即可

if [[ [=11=] -nt $scriptupdate ]]; then
  exec [=11=] $@
fi
 

Exec 将 运行ning 脚本替换为新脚本 [=13=]是当前脚本的完整路径和名称 $@ 将参数传递给新的 运行。