一个shell脚本能否指示它的行最初被加载到内存中?

15

更新:这是如何使shell脚本能够在运行时源代码被更改时依然稳定的重新发布的转载文章。

以下是我时不时会感到烦恼的小事:

  1. 我编写了一个用于快速而粗略工作的shell脚本(bash)
  2. 我运行脚本,它运行了相当一段时间
  3. 在脚本运行的同时,我编辑了几行代码,为不同的作业进行配置
  4. 但第一个进程仍在从相同的脚本文件中读取,并且出现了所有问题。

显然,脚本是通过每次从文件中加载每一行来解释的。是否有某种方法可以让脚本指示shell应一次性将整个脚本文件读入内存中?例如,Perl脚本似乎就是这样做的:编辑代码文件不会影响当前正在解释它的进程(因为它最初被解析/编译了?)。

我知道有很多方法可以解决这个问题。例如,我可以尝试类似于以下内容的东西:

cat script.sh | sh
或者
sh -c "`cat script.sh`"

尽管这些方法可能无法正确处理脚本文件非常大的情况,并且流缓冲区和命令行参数的大小存在限制。我也可以编写一个辅助包装器,将脚本文件复制到一个锁定的临时文件中,然后执行它,但这似乎并不是很通用。

因此,我希望找到最简单的解决方案,只需要对脚本进行修改,而不涉及调用方式。我能否在脚本开头加上一两行代码呢?我不知道是否存在这样的解决方案,但我猜想它可能会使用$0变量...


3
这是一个重复的问题,原链接为https://dev59.com/U3E95IYBdhLWcg3wf-AF。请问需要翻译哪些内容? - camh
谢谢,camh!这太完美了。 - A Real Live Operator
你可以在运行脚本时删除它,以避免编辑。(这可能是 ephemientRyan Thompson 想表达的意思。)请参考此帖子了解其原因和如何自动化。 - teika kazura
正如问题中所提到的,这与以下问题相同:如何使shell脚本在运行时对源代码的更改具有鲁棒性 - giraffesyo
7个回答

17

我找到的最佳答案与如何使shell脚本在运行时更加稳定问题中提供的解决方案几乎完全一样。感谢camh注意到了重新发布!

#!/bin/sh
{
    # Your stuff goes here
    exit
}

这确保了初步解析您所有的代码;请注意,“退出”对于确保文件不会被后续访问以查看是否有其他行需要解释至关重要。此外,如前面的帖子所指出的那样,这并不能保证由您的脚本调用的其他脚本是安全的。

感谢大家的帮助!


4

使用不会修改现有文件的编辑器,而是创建一个新文件来替换旧文件。例如,在Vim中使用:set writebackup backupcopy=no


2

希望您能够提供一种解决方法,以便您编辑它。

如果脚本正在运行,请在编辑之前执行以下操作:

mv script script-old
cp script-old script
rm script-old

只要您不更改打开的inode的内容,shell会一直保持文件处于打开状态,这样一切都会正常工作。

上述方法有效是因为mv会保留旧的inode,而cp会创建一个新的inode。由于如果文件已打开,则其内容实际上不会被删除,因此可以立即将其删除,一旦shell关闭文件,它就会被清理掉。


谢谢,这很有帮助!对我来说记住这个方法相当容易,但并不是最理想的解决方案:最好在脚本文件本身中包含一些内容,需要一次性读取整个文件。例如,如果多个开发人员正在维护同一个脚本,并且它可能运行很长时间,那么您需要确保所有开发人员每次编辑文件时都执行此mv-cp-rm技巧... - A Real Live Operator
1
稍微简化一下: cp 脚本 脚本-old; mv 脚本-old 脚本 - Roger Pate
你可以直接删除原始文件。请看我的对该问题的评论。 - teika kazura
我的答案包含脚本中的类似内容:https://dev59.com/VnE95IYBdhLWcg3wXs2R#70426690 - Acorn

1

根据Bash文档,如果不是

#!/bin/bash
body of script

你试试

#!/bin/bash
script=$(cat <<'SETVAR'
body of script
SETVAR)
eval "$script"

那么我想你就能成功了。


谢谢,Norman!如果您确保脚本以“exit”结束,则此方法完美运行。然而,它的缺点是您会失去在emacs中的漂亮打印颜色格式... - A Real Live Operator

1
一个使脚本免受这个问题影响的自包含方法是让脚本像这样复制并重新执行自己:
#!/bin/bash

if [[ $0 != /tmp/copy-* ]] ; then
    rm -f /tmp/copy-$$
    cp $0 /tmp/copy-$$
    exec /tmp/copy-$$ "$@"
    echo "error copying and execing script"
    exit 1
fi

rm $0

# rest of script...

如果原始脚本以字符/tmp/copy-开头,则此方法无效。

(这受到R Samuel Klatchko的答案启发)


0
考虑为您的快速脚本创建一个新的bang路径。如果您的脚本以以下方式开始: #!/usr/local/fastbash 或者其他什么,那么您可以编写一个fastbash包装器来使用您提到的方法之一。为了可移植性,可以将fastbash创建一个符号链接到bash,或在脚本中添加注释,说明可以将fastbash替换为bash。

0
如果您使用 Emacs,请尝试M-x customize-variable break-hardlink-on-save。设置此变量将告诉 Emacs 写入临时文件,然后将临时文件重命名为原始文件,而不是直接编辑原始文件。这应该允许运行实例保持其未修改版本,同时保存新版本。
可能,其他半智能编辑器也有类似的选项。

谢谢,这对于Emacs用户来说是一个很好的提示,我可能会从现在开始将其设置为默认设置。但仍然存在一个问题,即如何防御性地保护我的运行脚本免受他人编辑而被搞砸的风险... - A Real Live Operator
它不起作用。可能仅适用于打破硬链接,而不是用新的保存替换文件。 - teika kazura

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接