首页 > 解决方案 > 将 BASH 脚本转换为在 SH 上运行(通过 BusyBox)

问题描述

我有一个运行最新版本FreshTomato的华硕路由器- BusyBox 附带。

我需要运行一个考虑到 BASH 的脚本——它是这个脚本的改编版——但它无法运行并出现以下错误: 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”代码转换。

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

那么,我怎样才能让这个脚本在我的路由器上运行呢?我只看到两个选项,但我无法弄清楚如何实现:

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

标签: bashshparameter-expansion

解决方案


我怎样才能让这个脚本在我的路由器上运行?

这很容易,要么:

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

让 BusyBox 将脚本解释为 BASH 而不是 SH - 我可以为此使用特定的 shebang 吗?

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

Shebang 用于在不同的解释器下运行文件。Using#!/bin/bash将调用bash,这与任何与busybox相关的内容无关,并且busybox不会参与其中。

我该怎么做?

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

for i in $(seq 100); do
    optionvarname="foreign_option_${i}"        
    # https://stackoverflow.com/questions/3601515/how-to-check-if-a-variable-is-set-in-bash
    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}\""
      

推荐阅读