Ere*_*ted 5 linux shell bash buffer
我有一个运行“补丁”脚本的电路板。补丁脚本始终在后台运行,它是一个运行以下伪代码的 shell 脚本:
while true; do
# checks if a patch tar file exists and if yes then do patching
sleep 10
done
Run Code Online (Sandbox Code Playgroud)
该脚本位于 /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
Run Code Online (Sandbox Code Playgroud)
它用另一个脚本替换自己并从同一位置执行它。这样做会出现任何问题吗?
如果文件被就地写入替换(inode 保持不变),则任何打开该文件的进程在从该文件读取时都会看到新数据。如果通过取消旧文件的链接并创建一个同名的新文件来替换它,则索引节点号会发生变化,并且任何保持该文件打开的进程仍将拥有旧文件。
mv可能会执行任一操作,具体取决于文件系统之间是否发生移动...为了确保获得全新的文件,请首先取消链接或重命名原始文件。像这样的东西:
mv /opt/patch.sh /opt/patch.sh.old # or rm
mv /mnt/update/patch.sh /opt/patch.sh
Run Code Online (Sandbox Code Playgroud)
这样,即使在移动之后,正在运行的 shell 仍将拥有旧数据的文件句柄。
也就是说,据我测试,Bash 在执行任何循环之前都会读取整个循环,因此只要执行保持在循环内,对底层文件的任何更改都不会改变正在运行的脚本。(它必须在执行之前读取整个循环,因为最后可能会有影响整个循环的重定向。)
退出循环后,Bash 将读取指针移回到循环结束后的位置,然后从循环结束后的位置继续读取输入文件。
脚本中定义的任何函数也会加载到内存中,因此将脚本的主要逻辑放入函数中,并且仅在最后调用它将使脚本非常安全,不会对文件进行修改:
#!/bin/sh
main() {
do_stuff
exit
}
main
Run Code Online (Sandbox Code Playgroud)
不管怎样,测试脚本被覆盖时会发生什么并不难:
$ 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
Run Code Online (Sandbox Code Playgroud)
注释掉后rm old.sh,脚本将就地更改。如果没有注释,将创建一个新文件。(此示例部分依赖于new.sh大于old.sh,就好像它更短一样,shell 的读取位置将超过循环后新脚本的末尾。)
| 归档时间: |
|
| 查看次数: |
1989 次 |
| 最近记录: |