在 bash 我正在构建一个更新脚本,如何更新更新脚本
In bash i'm building an update script, how to update the updater script
我正在构建一个小脚本来更新 raspberry pi 上的应用程序文件。
它将执行以下操作:
- 下载应用程序文件的 zip 文件
- 解压缩它们
- 将每一个复制到正确的位置并根据需要使其可执行等。
我遇到的问题是其中一个文件是 updatescript.sh。
我了解到在 bash 脚本执行时更新/更改它是危险的。参见 Edit shell script while it's running
有什么好的方法可以实现我想要做的事情吗?
基本上一起做点什么:
shouldbe="/tmp/$(basename "[=10=]")"
if [ "[=10=]" != "$shouldbe" ]; then
cp "[=10=]" "$shouldbe"
exec env REALPATH="[=10=]" "$shouldbe" "$@"
fi
- 检查您是否运行来自临时目录
- 如果不是,请自己复制并从临时目录重新运行
您甚至可以通过使用环境变量或参数传递一些 variables/state。然后您可以使用简单的 cp
更新自己,因为旧路径不再是来源(甚至打开)。
cp "new_script_version.sh" "$REALPATH"
脚本如下所示:
#!/bin/bash
# we need to be run from /tmp directory
shouldbe="/tmp/$(basename "[=12=]")"
if [ "[=12=]" != "$shouldbe" ]; then
cp "[=12=]" "$shouldbe"
exec env REALPATH="[=12=]" "$shouldbe" "$@"
fi
echo "Updatting...."
echo "downloading zip files"
echo "unziping zip files..."
echo "Copying each zip files etc."
cp directory"new_updatescript.sh "$REALPATH"
echo "Update succedded"
Live/test 版本可在 tutorialspoint.
为了以防万一,还可以对脚本实施一些 flock
锁定。
你读到的内容被严重夸大了。
通过 mv
在其上覆盖一个不同的文件来就地覆盖 shell 脚本是完全安全的。 当您这样做时,旧的文件句柄仍然有效,指的是原始未修改的文件内容。您不能安全地做的是就地编辑现有文件。
因此,下面的内容很好(并且是所有 OS-供应商更新工具(如 RPM)的作用):
#!/usr/bin/env bash
tempfile=$(mktemp "$BASH_SOURCE".XXXXXX)
if curl https://example.com/whatever >"$tempfile" &&
curl https://example.com/whatever.sig >"$tempfile.sig" &&
gpgv "$tempfile.sig" "$tempfile"; then
chown --reference="$BASH_SOURCE" -- "$tempfile"
chmod --reference="$BASH_SOURCE" -- "$tempfile"
sync # force your filesystem to fully flush file contents to disk
mv -- "$tempfile" "$BASH_SOURCE" && rm -f -- "$tempfile.sig"
else
rm -f -- "$tempfile" "$tempfile.sig"
exit 1
fi
...然而这是有风险的:
curl https://example.com/whatever >/usr/local/bin/whatever
所以做第一件事,而不是第二件事:当下载新版本的脚本时,将其写入不同的文件,只有在下载成功后才将其重命名为原来的文件。这就是您无论如何要确保原子性的方法。
(上面还有一些代码签名验证实践的演示,因为在构建更新程序时您需要它们。您不会尝试通过自动下载分发代码而不验证一个签名,对吗?因为这就是对您的 Web 服务器的一次简单入侵会导致您的每个客户都被 0wned 的方式。上面期望您的代码签名密钥的 public 侧位于 ~/.gnupg/trustedkeys.gpg
, 但你可以将 trustedkeys.gpg
放在任何目录中并使用环境变量 GNUPGHOME
).
指向它
即使您没有安全地编写您的更新代码,风险仍然是微不足道的。如果您将脚本的主体移动到一个函数中,这样它在执行文件的任何部分之前必须完全读取,然后在执行开始时文件的任何部分都没有被读取。
#!/usr/bin/env bash
main() {
echo "Logic all goes here"
}; { main "$@"; exit; }
因为 { main "$@"; exit; }
是复合命令的一部分,解析器在开始执行 main
之前读取 exit
,因此可以保证不会再有源文件内容在 main
退出后阅读,即使将来的某些 bash 版本首先没有逐行处理输入。
我正在构建一个小脚本来更新 raspberry pi 上的应用程序文件。
它将执行以下操作:
- 下载应用程序文件的 zip 文件
- 解压缩它们
- 将每一个复制到正确的位置并根据需要使其可执行等。
我遇到的问题是其中一个文件是 updatescript.sh。
我了解到在 bash 脚本执行时更新/更改它是危险的。参见 Edit shell script while it's running
有什么好的方法可以实现我想要做的事情吗?
基本上一起做点什么:
shouldbe="/tmp/$(basename "[=10=]")"
if [ "[=10=]" != "$shouldbe" ]; then
cp "[=10=]" "$shouldbe"
exec env REALPATH="[=10=]" "$shouldbe" "$@"
fi
- 检查您是否运行来自临时目录
- 如果不是,请自己复制并从临时目录重新运行
您甚至可以通过使用环境变量或参数传递一些 variables/state。然后您可以使用简单的 cp
更新自己,因为旧路径不再是来源(甚至打开)。
cp "new_script_version.sh" "$REALPATH"
脚本如下所示:
#!/bin/bash
# we need to be run from /tmp directory
shouldbe="/tmp/$(basename "[=12=]")"
if [ "[=12=]" != "$shouldbe" ]; then
cp "[=12=]" "$shouldbe"
exec env REALPATH="[=12=]" "$shouldbe" "$@"
fi
echo "Updatting...."
echo "downloading zip files"
echo "unziping zip files..."
echo "Copying each zip files etc."
cp directory"new_updatescript.sh "$REALPATH"
echo "Update succedded"
Live/test 版本可在 tutorialspoint.
为了以防万一,还可以对脚本实施一些 flock
锁定。
你读到的内容被严重夸大了。
通过 mv
在其上覆盖一个不同的文件来就地覆盖 shell 脚本是完全安全的。 当您这样做时,旧的文件句柄仍然有效,指的是原始未修改的文件内容。您不能安全地做的是就地编辑现有文件。
因此,下面的内容很好(并且是所有 OS-供应商更新工具(如 RPM)的作用):
#!/usr/bin/env bash
tempfile=$(mktemp "$BASH_SOURCE".XXXXXX)
if curl https://example.com/whatever >"$tempfile" &&
curl https://example.com/whatever.sig >"$tempfile.sig" &&
gpgv "$tempfile.sig" "$tempfile"; then
chown --reference="$BASH_SOURCE" -- "$tempfile"
chmod --reference="$BASH_SOURCE" -- "$tempfile"
sync # force your filesystem to fully flush file contents to disk
mv -- "$tempfile" "$BASH_SOURCE" && rm -f -- "$tempfile.sig"
else
rm -f -- "$tempfile" "$tempfile.sig"
exit 1
fi
...然而这是有风险的:
curl https://example.com/whatever >/usr/local/bin/whatever
所以做第一件事,而不是第二件事:当下载新版本的脚本时,将其写入不同的文件,只有在下载成功后才将其重命名为原来的文件。这就是您无论如何要确保原子性的方法。
(上面还有一些代码签名验证实践的演示,因为在构建更新程序时您需要它们。您不会尝试通过自动下载分发代码而不验证一个签名,对吗?因为这就是对您的 Web 服务器的一次简单入侵会导致您的每个客户都被 0wned 的方式。上面期望您的代码签名密钥的 public 侧位于 ~/.gnupg/trustedkeys.gpg
, 但你可以将 trustedkeys.gpg
放在任何目录中并使用环境变量 GNUPGHOME
).
即使您没有安全地编写您的更新代码,风险仍然是微不足道的。如果您将脚本的主体移动到一个函数中,这样它在执行文件的任何部分之前必须完全读取,然后在执行开始时文件的任何部分都没有被读取。
#!/usr/bin/env bash
main() {
echo "Logic all goes here"
}; { main "$@"; exit; }
因为 { main "$@"; exit; }
是复合命令的一部分,解析器在开始执行 main
之前读取 exit
,因此可以保证不会再有源文件内容在 main
退出后阅读,即使将来的某些 bash 版本首先没有逐行处理输入。