Bash 会在保存时自动将更新重新加载(注入)到正在运行的脚本中:为什么?有什么实际用途吗?

aul*_*ron 12 shell scripting bash

我正在编写一个 bash 脚本,并且碰巧在脚本在while循环中等待某些输入时更新了代码(将脚本文件保存到磁盘)。在我返回终端并继续之前的脚本调用后,bash 给出了一个关于文件语法的错误:

/home/aularon/bin/script: line 58: unexpected EOF while looking for matching `"'
/home/aularon/bin/script: line 67: syntax error: unexpected end of file
Run Code Online (Sandbox Code Playgroud)

所以我尝试执行以下操作:

第一:创建一个脚本,self-update.sh我们称之为:

#!/bin/bash
fname=$(mktemp)
cat $0 | sed 's/BEFORE\./AFTER!./' > $fname
cp $fname $0
rm -f $fname
echo 'String: BEFORE.';
Run Code Online (Sandbox Code Playgroud)

脚本所做的是读取其代码,将单词“BEFORE”更改为“AFTER”,然后使用新代码重新编写自己。

2运行它:

chmod +x self-update.sh
./self-update.sh
Run Code Online (Sandbox Code Playgroud)

第三个奇迹...

aularon@aularon-laptop:~$ ./self-update.sh 
String: AFTER!.
Run Code Online (Sandbox Code Playgroud)

现在,我不会猜到在同一次调用中它会输出AFTER!,肯定会在第二次运行,但不会在第一次运行。

所以我的问题是:这是有意的(按设计)吗?还是因为 bash 运行脚本的方式?逐行或逐个命令。这种行为有什么用处吗?有什么例子吗?


编辑:我尝试重新格式化文件以将所有命令放在一行中,现在不起作用:

#!/bin/bash
fname=$(mktemp);cat $0 | sed 's/BEFORE\./AFTER!./' > $fname;cp $fname $0;rm -f $fname;echo 'String: BEFORE.';
Run Code Online (Sandbox Code Playgroud)

输出:

aularon@aularon-laptop:~$ ./self-update.sh #First invocation
String: BEFORE.
aularon@aularon-laptop:~$ ./self-update.sh #Second invocation
String: AFTER!.
Run Code Online (Sandbox Code Playgroud)

当将echo字符串移动到下一行时,将其与重写 ( cp) 调用分开:

#!/bin/bash
fname=$(mktemp);cat $0 | sed 's/BEFORE\./AFTER!./' > $fname;cp $fname $0;rm -f $fname;
echo 'String: BEFORE.';
Run Code Online (Sandbox Code Playgroud)

现在它又可以工作了:

aularon@aularon-laptop:~$ ./self-update.sh 
String: AFTER!.
Run Code Online (Sandbox Code Playgroud)

phe*_*mer 15

这是设计使然。Bash 分块读取脚本。所以它会读取脚本的一部分,运行它可以运行的任何行,然后读取下一个块。

所以你遇到了这样的事情:

  • Bash 读取脚本的前 256 个字节(字节 0-255)。
  • 在前 256 个字节中是一个需要运行一段时间的命令,bash 启动该命令,等待它退出。
  • 当命令运行时,脚本被更新,改变的部分它已经读取的 256 字节之后
  • 当命令 bash 运行完成时,它继续读取文件,从原来的位置恢复,获取字节 256-511。
  • 脚本的那部分更改了,但 bash 不知道。

这变得更成问题的地方在于,如果您在字节 256 之前编辑任何内容。假设您删除了几行。然后脚本中位于字节 256 的数据现在位于其他位置,比如字节 156(早 100 个字节)。因此,当 bash 继续阅读时,它将获得最初的 356。

这只是一个例子。Bash 不一定一次读取 256 个字节。我不知道它一次读取多少,但没关系,行为仍然相同。