在 SH 上将 BASH 脚本转换为 运行(通过 BusyBox)

Converting a BASH script to run on SH (via BusyBox)

我有一个 Asus 路由器 运行正在使用最新版本的 FreshTomato - BusyBox 随附。

我需要 运行 一个考虑到 BASH 的脚本 - 它是对 this script 的改编 - 但它无法 运行 并出现此错误: line 41: syntax error: bad substitution

使用 shellcheck.net 检查脚本会产生以下错误:

Line 41:
        for optionvarname in ${!foreign_option_*} ; do
                             ^-- SC3053: In POSIX sh, indirect expansion is undefined.
                             ^-- SC3056: In POSIX sh, name matching prefixes are undefined.
 
Line 42:
        option="${!optionvarname}"
                ^-- SC3053: In POSIX sh, indirect expansion is undefined.

这些是导致问题的行:

for optionvarname in ${!foreign_option_*} ; do   # line 41
    option="${!optionvarname}"                   # line 42

    # do some stuff with $option...
done

如果我的理解是正确的,原始脚本只是对名称以 foreign_option_

开头的所有变量做一些事情

然而,据我所知,${!foreign_option_*}${!optionvarname} 结构都是 BASH 特定的,而不是 POSIX 兼容的,所以没有直接的“ bash 到 sh" 代码转换可能。

我试图创建一个指向 busybox/bin/bash 符号链接,但我遇到了 Read-only file system 错误。

那么,我怎样才能将此脚本发送到我的路由器上的 运行?我只看到两个选项,但我不知道如何实现:

  1. 让 BusyBox 将脚本解释为 BASH 而不是 SH - 我可以为此使用特定的 shebang 吗?
    • 似乎是最快的选择,但前提是 BusyBox 具有 BASH
    • 的“完整”实现
  2. 更改脚本代码以不使用 BASH 细节。
    • 这样比较安全,但是SH没有“收集所有以X开头的变量”,我该怎么做呢?

how can I get this script to run on my router?

很简单,要么:

  • 在您的路由器上安装 bash 或
  • 将脚本移植到 busybox/posix 兼容 shell。

Make BusyBox interpret the script as BASH instead of SH - can I use a specific shebang for this?

这没有意义。 Busybox 自带 ash shell 解释器而 bash 是 bash。 Bash 可以解释 bash 扩展名,ash 不能解释它们。你不能“让 busybox 解释 bash”——汽车不会飞,飞机是用来飞的。如果你想让汽车飞起来,你就给它装上翅膀让它跑得更快。 Make BusyBox interpret the script as BASH instead of SH 的答案是:修补 busybox 并在其中实现所有 bash 扩展。

Shebang 用于在不同 解释器下运行 文件。使用 #!/bin/bash 会调用 bash,这与任何与 busybox 相关的东西都无关,busybox 也不会参与其中。

how can I do it?

确定一个不切实际的最大值,遍历名为 foreign_option_{1...some_max} 的变量,对于每个变量,查看它是否已设置,如果已设置,则继续脚本。

for i in $(seq 100); do
    optionvarname="foreign_option_${i}"        
    # 
    if eval "[ -z \"${${optionvarname}+x}\" ]"; then continue; fi;

有足够的运气也许你可以使用set输出。如果任何变量包含一个值作为换行符 + 与正则表达式匹配的字符串,则以下将失败:

for optionvarname in $(set | grep -o '^foreign_option_[0-9]\+=' | sed 's/=//'); then

间接扩展可以很容易地替换为eval:

eval "option=\"$${optionvarname}\""
      

如果您真的无法在该路由器上安装 Bash,这里有一个可能的解决方法,它似乎适用于 Qnap NAS 上的 BusyBox:

foreign_option_one=1
foreign_option_two=2

for x in one two; do
    opt_var=foreign_option_${x}
    eval "opt_value=$$opt_var"
    echo "$opt_var = $opt_value"
done

(但是将 Bash 脚本移动到 busybox 可能会遇到更多问题,因此您可能需要首先考虑替代方案,例如更换路由器)