覆盖执行bash脚本文件

时间:2014-01-13 16:35:17

标签: bash

如果覆盖执行的bash脚本文件会发生什么?它会继续执行旧文件还是覆盖文件?

例如 /etc/test.sh具有以下内容

cp /test.sh /etc/test.sh

3 个答案:

答案 0 :(得分:9)

bash通常提前加载整个脚本,因此覆盖脚本会产生不可预测的后果。例如,我刚刚创建了一个包含以下内容的脚本:

#!/bin/bash
echo start old file
sleep 20
echo end old file

......跑了。在它睡觉的时候,我用这个略有不同的剧本覆盖了它:

#!/bin/bash
echo started new file
sleep 20
echo end new file

导致此输出:

$ ./test.sh 
start old file
./test.sh: line 4: 0: command not found
end new file

发生了什么事情是bash在睡觉前通过“睡眠20 \ n”线(包括终止换行的行)读过。当它继续之后,它从文件中的下一个字节位置读取下一个命令,并且因为第二行已经长了两个字节(“start” - >“started”),所以最后读取新睡眠行的最后两个字节(“睡眠20 \ n” - >>“0 \ n”),尝试执行“0”作为命令,并收到错误。然后它运行新内容的最后一行。很乱,对吧?

幸运的是,有一种方法可以强制bash将整个脚本加载到内存中,这样如果在运行时它被改变就不会感到困惑:

#!/bin/bash
{
    echo start old file
    sleep 20
    echo end old file
    exit
}

{强制shell至少通过匹配的}读取以正确解析命令,然后exit确保它永远不会尝试执行任何操作在}之后添加。

BTW,我用bash版本3.2.48(1)-release(OS X 10.8.5)测试了这个;其他版本可能表现不同......

更新:其他版本确实表现不同。我尝试使用版本4.3.0(1)-release(在NetBSD 6.1.4下)进行相同的测试,并在文件内容被替换后继续运行旧代码。显然它现在以8KB块的形式缓存文件(参见this unix.se answer)。

答案 1 :(得分:1)

shell将脚本加载到内存中并生成一个单独的进程,因此旧脚本将继续运行 unmodified

显然,如果您尝试重新运行它,您将获得新版本。

如果您尝试在运行时引入一些新功能,我建议写入临时文件,然后使用$()或``(反引号)语法执行该文件以生成新进程。

或者,您可以使用exec完全替换当前正在运行的程序。

答案 2 :(得分:0)

另一种机制与Donovan提到的类似,只是将cat文件stdout并通过管道传输到bash中:

cat my_script.sh | bash

但是,我不知道在给定的OS上是否存在管道缓冲的陷阱。 (我在Ubuntu 18.04上)。

当然,这仅适用于仅依赖于一个正在修改的源文件的脚本。