运行时替换 shell 脚本

运行时替换 shell 脚本

我有一个正在运行“补丁”脚本的板。补丁脚本始终在后台运行,它是一个运行以下伪代码的 shell 脚本:

while true; do
    # checks if a patch tar file exists and if yes then do patching
    sleep 10
done

该脚本位于/opt/patch.sh,由SystemV init 脚本启动。

问题是,当脚本找到 tar 时,它会提取它,并且里面有一个名为的 shell 脚本补丁文件这是特定于 tar 的内容。

当脚本位于/opt/patch.sh找到 tar 它会执行以下操作:

tar -xf /opt/update.tar -C /mnt/update
mv /mnt/update/patch.sh /opt/patch.sh
exec /opt/patch.sh

它用另一个脚本替换自身并从同一位置执行它。这样做会出现什么问题吗?

答案1

如果文件被就地写入替换(inode 保持不变),则任何打开该文件的进程在从该文件读取时都会看到新数据。如果通过取消旧文件的链接并创建一个同名的新文件来替换它,则索引节点号会发生变化,并且任何保持该文件打开的进程仍将具有老的文件。

mv可能会执行任一操作,具体取决于文件系统之间是否发生移动...为了确保获得全新的文件,请首先取消链接或重命名原始文件。像这样的东西:

mv /opt/patch.sh /opt/patch.sh.old     # or rm
mv /mnt/update/patch.sh /opt/patch.sh

这样,即使在移动之后,正在运行的 shell 仍将拥有旧数据的文件句柄。


也就是说,据我测试,Bash 在执行任何循环之前都会读取整个循环,因此只要执行保持在循环内,对底层文件的任何更改都不会改变正在运行的脚本。 (它必须在执行之前读取整个循环,因为最后可能会有影响整个循环的重定向。)

退出循环后,Bash 将读取指针移回到循环结束后的位置,然后从循环结束后的位置继续读取输入文件。

脚本中定义的任何函数也会加载到内存中,因此将脚本的主要逻辑放入函数中,并且仅在最后调用它将使脚本非常安全,不会对文件进行修改:

#!/bin/sh
main() {
    do_stuff
    exit
}
main

不管怎样,测试脚本被覆盖时会发生什么并不难:

$ cat > old.sh <<'EOF'
#!/bin/bash
for i in 1 2 3 4 ; do
        # rm old.sh
        cat new.sh > old.sh 
        sleep 1
        echo $i
done
echo will this be reached?
EOF
$ cat > new.sh <<'EOF'
#!/bin/bash
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
EOF
$ bash old.sh

注释掉后rm old.sh,脚本将就地更改。如果没有注释,将创建一个新文件。 (此示例部分依赖于new.sh大于old.sh,就好像它更短一样,shell 的读取位置将超过循环后新脚本的末尾。)

答案2

我以前遇到过这个问题,并且可以确认这可能是一个问题。就我而言,回归脚本首先执行 git pull,并可能在开始运行后进行更新,从而导致问题。

问题通常是 shell 会返回并检查是否还有更多行需要解释。即使所需的代码位于循环内,这也可能会导致错误。为了避免这种情况,请使用以下结构这个帖子

答案3

一个自动执行、自动修改的脚本?这不是一个好主意。

更好的解决方案是创建一个具有最少功能的存根守护程序(即负责安装新版本的从属脚本并定期调用它)。类似...(未测试)

while true; do
  # check if a patch tar file exists and if yes then do patching
  if [ -f "$PATCH" ]; then
      ( cd /usr/local/mydaemon \
      && tar -xzf "$PATCH" \
      && rm -f "$PATCH" ) \
      || exit -1
  fi
  $SCRIPT
  sleep 10
done

相关内容