如何根据另一个变量简洁地为变量分配不同的值?

IIS*_*eII 20 shell-script

我怎样才能缩短这个 shell 脚本?

CODE="A"

if test "$CODE" = "A"
then
 PN="com.tencent.ig"
elif test "$CODE" = "a"
 then
 PN="com.tencent.ig"
elif test "$CODE" = "B"
 then
 PN="com.vng.pubgmobile"
elif test "$CODE" = "b"
 then
 PN="com.vng.pubgmobile"
elif test "$CODE" = "C"
 then
 PN="com.pubg.krmobile"
elif test "$CODE" = "c"
 then
 PN="com.pubg.krmobile"
elif test "$CODE" = "D"
 then
 PN="com.rekoo.pubgm"
elif test "$CODE" = "d"
 then
 PN="com.rekoo.pubgm"
else
 echo -e "\a\t ERROR!"
 echo -e "\a\t CODE KOSONG"
 echo -e "\a\t MELAKUKAN EXIT OTOMATIS"
 exit
fi
Run Code Online (Sandbox Code Playgroud)

Gor*_*son 62

使用case语句(可移植,适用于任何类似shshell):

case "$CODE" in
    [aA] ) PN="com.tencent.ig" ;;
    [bB] ) PN="com.vng.pubgmobile" ;;
    [cC] ) PN="com.pubg.krmobile" ;;
    [dD] ) PN="com.rekoo.pubgm" ;;
    * ) printf '\a\t%s\n' 'ERROR!' 'CODE KOSONG' 'MELAKUKAN EXIT OTOMATIS' >&2
        exit 1 ;;
esac
Run Code Online (Sandbox Code Playgroud)

我还建议将变量名称从所有大写字母(如CODE)更改为小写或混合大小写(如codeCode)。有很多全大写的名字都有特殊的含义,不小心重复使用其中一个会造成麻烦。

其他注意事项:标准约定是将错误信息发送到“标准错误”而不是“标准输出”;该>&2重定向做到这一点。此外,如果脚本(或程序)失败,最好以非零状态 ( exit 1)退出,这样任何调用上下文都可以知道哪里出了问题。它也可以使用不同的状态来表示不同的问题(见的“退出代码”一节curl手册页的一个很好的例子)。(感谢 Stéphane Chazelas 和 Monty Harder 在此处提供建议。)

我推荐printf而不是echo -e(and echo -n),因为它在操作系统、版本、设置等之间更具可移植性。我曾经有一堆脚本中断,因为操作系统更新包含一个使用不同选项编译的 bash 版本,这改变了echo行为方式。

$CODE这里不需要双引号。a 中的字符串case是少数几个可以安全地将它们放在一边的上下文之一。然而,我更喜欢双引号变量引用,除非有特定的理由不这样做,因为很难跟踪它在哪里是安全的,哪里不安全,所以习惯性地双引号它们更安全。

  • 这正是执行此操作的正确方法,直到最后的通配符将其输出重定向到 stderr 并生成非零退出值。唯一可能需要更改的是退出值,因为返回的错误可能不止一个。在更大的脚本中,可能有一个部分(可能来自另一个文件)定义退出值 `readonly Exit_BadCode=1`,以便它可以改为说 `exit $Exit_BadCode`。 (6认同)
  • @IISomeOneII 这将算作`*`(并打印错误)——模式`[aA]` 匹配“a”或“A”,但不能同时匹配。 (5认同)
  • 如果使用最近的 bash,则使用 `case "${CODE,}" in`,这样每个条件就变成简单的 `a)`、`b)` 等。 (2认同)
  • @MontyHarder 这取决于。如果有几百个这样的代码,每个对应一个字符串,那么另一种方法可能会更好。对于手头的确切问题,这已经足够了。 (2认同)
  • @MontyHarder 对不起,我应该更清楚。“代码”是指`$CODE`。我总是这样称呼“退出状态”,而不仅仅是“代码”。如果脚本需要使用数百个 _keys_ 来引用字符串,则使用 `case` 语句变得笨拙。 (2认同)
  • @GordonDavisson,OTOH,如果您仅限于 POSIX sh,则可以将 case 语句放在函数中。 (2认同)

Kus*_*nda 19

假设您使用的是bash4.0 版或更新版本...

CODE=A

declare -A domain

domain=(
   [a]=com.tencent.ig
   [b]=com.vng.pubgmobile
   [c]=com.pubg.krmobile
   [d]=com.rekoo.pubgm
)

PN=${domain[${CODE,,}]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}
Run Code Online (Sandbox Code Playgroud)

在代码中,我定义了一个包含所有域名的关联数组,每个域名都与一个小写字母键相关联。

$PN变量被分配对应于小写域名$CODE值(${CODE,,}返回的值$CODE仅变成小写字母)从该阵列中,但如果$CODE对应不到在一个有效项domain列表时,它离开所述脚本用错误。

${variable:?error message}参数替换将扩大到的值$variable(在代码中适当的域),但如果该值是空不可将退出与所述错误消息的脚本。您不会获得与代码中完全相同的错误消息格式,但如果无效,它的行为本质上相同的$CODE

$ bash script.sh
script.sh: line 12: domain[${CODE,,}]: ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS
Run Code Online (Sandbox Code Playgroud)

如果您关心字符数,我们可以进一步缩短它:

CODE=A
declare -A domain=( [a]=tencent.ig [b]=vng.pubgmobile [c]=pubg.krmobile [d]=rekoo.pubgm )
PN=com.${domain[${CODE,,}]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}
Run Code Online (Sandbox Code Playgroud)

除了删除不必要的换行符之外,我还com.从每个域中删除了(改为添加到 的分配中PN)。

请注意,上面的所有代码甚至适用于多字符值$CODE(如果domain数组中存在小写键)。


如果$CODE是数字(从零开始)索引,这将稍微简化代码:

CODE=0

domain=( com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm )
PN=${domain[CODE]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}
Run Code Online (Sandbox Code Playgroud)

此外,这将使domain从每行包含一个条目的辅助文件中读取数组变得非常容易:

CODE=0

readarray -t domain <domains.txt
PN=${domain[CODE]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}
Run Code Online (Sandbox Code Playgroud)


ImH*_*ere 11

如果您的 shell 允许数组,那么最短的答案应该是 bash 中的这个示例:

declare -A site
site=( [a]=com.tencent.ig [b]=com.vng.pubgmobile [c]=com.pubg.krmobile [d]=com.rekoo.pubgm )

pn=${site[${code,}]}
Run Code Online (Sandbox Code Playgroud)

那是假设$code只能是 a、b、c 或 d。
如果没有,请添加如下测试:

case ${site,} in
    a|b|c|d)        pn=${site[${code,}]};;
    *)              pn="default site"
                    printf '\a\t %s\n' 'ERROR!' 'CODE KOSONG' 'MELAKUKAN EXIT OTOMATIS'
                    exit 1
                    ;;
esac
Run Code Online (Sandbox Code Playgroud)

  • 是的,扩展 `${var,}` 将 `${var}` 的第一个字符转换为小写。@IISomeOneII (2认同)