我有一个运行“补丁”脚本的电路板.补丁脚本始终在后台运行,它是一个运行以下伪代码的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时,它会提取它,并且里面有一个名为patch.sh的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
它将替换为另一个脚本并从同一位置执行它.
这样做会出现任何问题吗?
解决方法:
如果通过就地写入来替换文件(inode保持不变),那么打开它的任何进程都会在从文件读取时查看新数据.如果通过取消链接旧文件并创建具有相同名称的新文件来替换它,则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 <
#!/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 <
#!/bin/bash
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
EOF
$bash old.sh
随着rm old.sh注释掉,脚本将就地更改.如果没有评论,将创建一个新文件.
标签:bash,linux,shell,buffer
来源: https://codeday.me/bug/20190813/1647647.html