如何在执行shell脚本之前读取整个shell脚本?

如何在执行shell脚本之前读取整个shell脚本?

通常,如果您编辑脚本,该脚本的所有运行用法都容易出错。

例子:

sleep 20

echo test

如果执行此脚本,bash 将读取第一行(例如 10 个字节)并进入睡眠状态。当它恢复时,脚本中从第 10 个字节开始可能有不同的内容。它可能会在完全不同的分支中开始执行不同行的中间if。正在运行的脚本将会被破坏。

那么,如何在执行之前读取整个shell脚本,以便以后的编辑不影响正在运行的实例呢?

答案1

是的,shellbash会特别小心地一次一行地读取文件,因此它的工作方式与交互使用它时的工作方式相同。

您会注意到,当文件不可查找时(如管道),bash甚至一次读取一个字节以确保不会读取超过该\n字符。当文件可查找时,它会通过一次读取完整块进行优化,但会返回到\n.

这意味着您可以执行以下操作:

bash << \EOF
read var
var's content
echo "$var"
EOF

或者编写自我更新的脚本。如果它没有给你这样的保证,你就无法做到这一点。

现在,您很少想做这样的事情,而且正如您发现的那样,该功能往往会比它的用处更频繁地妨碍您。

为了避免这种情况,您可以尝试确保不就地修改文件(例如,修改副本,然后将副本移动到位(例如sed -iperl -pi某些编辑器所做的那样))。

或者你可以这样写你的脚本:

{
  sleep 20
  echo test
}; exit

(请注意,重要的是exit与 ; 位于同一行},尽管您也可以将其放在大括号内,就在结束括号之前)。

或者:

main() {
  sleep 20
  echo test
}
main "$@"; exit

在开始执行任何操作之前,shell 需要读取脚本exit。这确保 shell 不会再次从脚本中读取。

这意味着整个脚本将存储在内存中。

这也会影响脚本的解析。

例如,在bash

export LC_ALL=fr_FR.UTF-8
echo $'St\ue9phane'

将输出以 UTF-8 编码的 U+00E9。但是,如果将其更改为:

{
  export LC_ALL=fr_FR.UTF-8
  echo $'St\ue9phane'
}

\ue9在解析命令时有效的字符集中进行扩展,在本例中为命令export被执行。

另请注意,如果使用sourceaka.命令,对于某些 shell,源文件也会遇到同样的问题。

bash但在解释文件之前,它的source命令会完全读取文件,但情况并非如此。如果bash专门编写,您实际上可以通过在脚本开头添加以下内容来利用它:

if [[ ! $already_sourced ]]; then
  already_sourced=1
  source "$0"; exit
fi

(我不会依赖这一点,尽管你可以想象未来的版本bash可能会改变这种行为,这目前可以被视为一种限制(bash 和 AT&T ksh 是唯一表现得像 POSIX 的 shell,据我们所知)这个already_sourced技巧有点脆弱,因为它假设变量不在环境中,更不用说它会影响 BASH_SOURCE 变量的内容)

答案2

您只需删除该文件(即复制它,删除它,将副本重命名回原始名称)。事实上,许多编辑器都可以配置为您执行此操作。当您编辑文件并将更改的缓冲区保存到其中时,它不会覆盖该文件,而是重命名旧文件,创建一个新文件,并将新内容放入新文件中。因此,任何正在运行的脚本都应该毫无问题地继续。

通过使用简单的版本控制系统(如 RCS)(可随时用于 vim 和 emacs),您可以获得拥有更改历史记录的双重优势,并且签出系统应默认删除当前文件并使用正确的模式重新创建它。 (当然要注意硬链接此类文件)。

答案3

使用:

{
  ... your code ...

  exit
}

{}Bash 将在执行之前读取整个块,并且该exit指令将确保不会读取代码块之外的任何内容。

对于“获取”而不是执行的脚本,请使用:

{
  ... your code ...

  return 2>/dev/null || exit
}

答案4

将脚本包装在块中{}可能是最好的选择,但需要更改脚本。

F=$(mktemp) && cp test.sh $F && bash $F; rm $F;

将是第二个最佳选择(假设临时文件系统)缺点是如果你的脚本使用它,它会破坏 $0。

使用类似的东西F=test.sh; tail -n $(cat "$F" | wc -l) "$F" | bash不太理想,因为它必须将整个文件保留在内存中并破坏 $0。

应避免接触原始文件,以便上次修改时间、读锁定和硬链接不会受到干扰。这样,您可以在运行文件时保持编辑器打开,并且 rsync 不会不必要地对文件进行校验和以进行备份,并且硬链接功能将按预期运行。

在编辑时替换文件可以工作,但不太健壮,因为它不能对其他脚本/用户执行/或者人们可能会忘记。它会再次破坏硬链接。

相关内容