【问题标题】:A way to prevent certain variables and strings being used in a 'For loop'一种防止在“For循环”中使用某些变量和字符串的方法
【发布时间】:2020-11-29 09:53:33
【问题描述】:

我必须编写一个忽略变量中特定字符串的 bash 脚本,以防止文件混淆。我对 bash 很陌生,我不知道如何编写一行代码来检查非法变量/字符串组合。

例如,字符串marbleigneous_rocks 决不能在for 循环中一起使用。这是需要更改的代码:

    #!/bin/bash

IGNEOUS_BLOCK=(aa adakite pahoehoe)
METAMORPHIC_BLOCK=(eclogite marble)
SEDIMENTARY_BLOCK=(argillite chalk jaspillite)
ROCK_TYPE=(igneous_rocks metamorphic_rocks sedimentary_rocks)

#Buttons
  for igneous_block in "${IGNEOUS_BLOCK[@]}" ; do
  for rock_type in "${ROCK_TYPE[@]}" ; do
      printf "{
    \"parent\": \"block/button\",
    \"textures\": {
        \"texture\": \"strata:blocks/"$rock_type"/"$igneous_block"\"
    }
}"> "${igneous_block}_button.json"
  done;
  done;

这是它应该做的:

如果变量ROCK_TYPE 使用字符串igneous_rocks,它应该只选择IGNEOUS_BLOCK 中的字符串,而不是来自METAMORPHIC_BLOCKSEDIMENTARY_BLOCK

这就是我希望所有变量都起作用的原因:

ROCK_TYPE 循环浏览可用的字符串。

ROCK_TYPE=(igneous_rocks metamorphic_rocks sedimentary_rocks)

应该只允许IGNEOUS_BLOCK 使用字符串igneous_rocks

IGNEOUS_BLOCK=(aa adakite pahoehoe)

应该只允许METAMORPHIC_BLOCK 使用字符串metamorphic_rocks

METAMORPHIC_BLOCK=(aa adakite pahoehoe)

SEDIMENTARY_BLOCK 应该只允许使用字符串sedimentary_rocks

SEDIMENTARY_BLOCK=(aa adakite pahoehoe)

需要对我的代码进行哪些更改才能使其按我想要的方式工作?

【问题讨论】:

  • 两个fordo 但只有一个done?添加一个shebang,然后将您的脚本粘贴到那里:shellcheck.net
  • 我将代码更改为有两个 done 而不是一个,并在顶部添加了一个 shebang,但我仍然最终将 'sedimentary_rocks` 与 'aa' 一起使用。我该如何解决?
  • 继续通过 ShellCheck。 shebang 不应该缩进,双引号是错误的。但这似乎不是问题。
  • 在 bash 中执行代码似乎完全没问题。 ShellCheck 只推荐替代方案来编写代码。文件确实按应有的方式生成,但在代码中使用的字符串的顺序是错误的。此外,双引号可以以多种方式使用。我还发现您可以将大部分代码用双引号或单引号括起来。
  • 是的,我问,为什么变量没有命名为igneous_rocks=(aa adakite pahoehoe),而是一些随机的IGNEOUS_BLOCK?您为什么要将igneous_rocks 转换为IGNEOUS_BLOCK?为什么不首先将变量命名为igneous_rocks

标签: string bash for-loop variables


【解决方案1】:

您似乎想要嵌套数据,其中ROCK_TYPE 是一个类型数组,每个类型都有一个子类型数组。

在真正的编程语言中,您可以使用嵌套数据结构,这很容易,但不幸的是,Bash 只有 ugly solutions 用于模拟嵌套数据。

相反,您可以使用case 语句来定义要迭代的块数组:

#!/bin/bash

rock_types=(igneous_rocks metamorphic_rocks sedimentary_rocks)

for rock_type in "${rock_types[@]}"; do
    case $rock_type in
    igneous_rocks)
        blocks=(aa adakite pahoehoe)
        ;;
    metamorphic_rocks)
        blocks=(eclogite marble)
        ;;
    sedimentary_rocks)
        blocks=(argillite chalk jaspillite)
        ;;
    esac
    for block in "${blocks[@]}"; do
        echo "$rock_type/$block"
    done
done

输出:

igneous_rocks/aa
igneous_rocks/adakite
igneous_rocks/pahoehoe
metamorphic_rocks/eclogite
metamorphic_rocks/marble
sedimentary_rocks/argillite
sedimentary_rocks/chalk
sedimentary_rocks/jaspillite

顺便说一句,应为 PATH 等环境变量和 RANDOM 等其他特殊变量保留全大写变量名。

【讨论】:

    【解决方案2】:

    假设:

    • OP 想要循环遍历 ROCK_TYPE 数组中列出的所有岩石类型
    • 每个岩石类型和匹配的 *_BLOCK 数组都有匹配的名称,a) 数组名称全部大写 b) 字符串 rocks 替换为 BLOCK

    一种相对简单的方法是使用case 语句根据当前岩石类型确定要引用的**_BLOCK 数组。

    验证想法的第一步:

    for rtype in "${ROCK_TYPE[@]}"
    do
        echo "+++++ rock type = ${rtype}"
    
        case "${rtype}" in
            igneous_rocks)          printf "%s\n" "${IGNEOUS_BLOCK[@]}"     ;;
            metamorphic_rocks)      printf "%s\n" "${METAMORPHIC_BLOCK[@]}" ;;
            sedimentary_rocks)      printf "%s\n" "${SEDIMENTARY_BLOCK[@]}" ;;
        esac
    done
    

    生成:

    +++++ rock type = igneous_rocks
    aa
    adakite
    pahoehoe
    +++++ rock type = metamorphic_rocks
    eclogite
    marble
    +++++ rock type = sedimentary_rocks
    argillite
    chalk
    jaspillite
    

    有几种方法可以修改它以允许循环访问关联的 *_BLOCK 数组中的各个值,例如:

    for rtype in "${ROCK_TYPE[@]}"
    do
            bname=${rtype//rocks/block}        # OPs example could use this for the 'texture' field and json file name
    
            for btype in $(case "${rtype}" in
                            igneous_rocks)          echo "${IGNEOUS_BLOCK[@]}"     ;;
                            metamorphic_rocks)      echo "${METAMORPHIC_BLOCK[@]}" ;;
                            sedimentary_rocks)      echo "${SEDIMENTARY_BLOCK[@]}" ;;
                           esac)
            do
                    echo ".${rtype}.${bname}.${btype}."
            done
    done
    

    生成:

    .igneous_rocks.igneous_block.aa.
    .igneous_rocks.igneous_block.adakite.
    .igneous_rocks.igneous_block.pahoehoe.
    .metamorphic_rocks.metamorphic_block.eclogite.
    .metamorphic_rocks.metamorphic_block.marble.
    .sedimentary_rocks.sedimentary_block.argillite.
    .sedimentary_rocks.sedimentary_block.chalk.
    .sedimentary_rocks.sedimentary_block.jaspillite.
    

    注意:句点作为视觉分隔符添加


    另一种想法是使用nameref(即,一个变量的值用作另一个变量的名称)。

    注意:我包含此选项是因为该示例显示了类似命名的岩石类型和块名称,这反过来使nameref 解决方案相对容易实现。

    考虑以下(简单)示例:

    $ unset x h
    $ x=5 h=x
    $ echo $x
    5
    $ echo $h
    x
    $ typeset -n h       # define nameref
    $ echo $h
    5
    $ x=10
    $ echo $h
    10
    

    或者:

    $ unset x h
    $ x=5
    $ typeset -n h=x     # define nameref
    $ echo $x
    5
    $ echo $h
    5
    $ x=10
    $ echo $h
    10
    

    关于使用namerefs 解决岩石/方块问题的一个想法:

    for rtype in "${ROCK_TYPE[@]}"
    do
            bname=${rtype//rocks/block}                     # again, use this for the 'texture' field and json file name
    
            typeset -u -n currblock=${rtype//rocks/BLOCK}   # define nameref, convert to all uppercase and replace 'rocks' with 'BLOCK'
    
            for btype in "${currblock[@]}"                  # loop through nameref'd values
            do
                    echo ".${rtype}.${bname}.${btype}."
            done
    done
    

    生成与case 解决方案相同的一组输出:

    .igneous_rocks.igneous_block.aa.
    .igneous_rocks.igneous_block.adakite.
    .igneous_rocks.igneous_block.pahoehoe.
    .metamorphic_rocks.metamorphic_block.eclogite.
    .metamorphic_rocks.metamorphic_block.marble.
    .sedimentary_rocks.sedimentary_block.argillite.
    .sedimentary_rocks.sedimentary_block.chalk.
    .sedimentary_rocks.sedimentary_block.jaspillite.
    

    注意:同样,句点作为视觉分隔符添加

    【讨论】:

      【解决方案3】:

      以下代码:

      IGNEOUS_BLOCK=(aa adakite pahoehoe)
      METAMORPHIC_BLOCK=(eclogite marble)
      SEDIMENTARY_BLOCK=(argillite chalk jaspillite)
      ROCK_TYPE=(igneous_rocks metamorphic_rocks sedimentary_rocks)
      for i in "${ROCK_TYPE[@]}"; do
          # convert to upercase, replace rocks
          n="${i//_rocks/_BLOCK}"
          n="${n^^}[@]"
          # Using indirect expansion
          for j in "${!n}"; do
             echo "$i/$j"
          done
      done
      

      会输出:

      igneous_rocks/aa
      igneous_rocks/adakite
      igneous_rocks/pahoehoe
      metamorphic_rocks/eclogite
      metamorphic_rocks/marble
      sedimentary_rocks/argillite
      sedimentary_rocks/chalk
      sedimentary_rocks/jaspillite
      

      注意:大写变量按惯例用于导出的变量,如 PWD LINES COLUMNS UID EID TERM LC_ALL COUNTRY HISTSIZE 等。最好在脚本中使用小写变量。

      你可以像数组元素一样命名变量,省去一些代码:

      igneous_rocks=(aa adakite pahoehoe)
      metamorphic_rocks=(eclogite marble)
      sedimentary_rocks=(argillite chalk jaspillite)
      rock_types=(igneous_rocks metamorphic_rocks sedimentary_rocks)
      for i in "${rock_types[@]}"; do
          n="${i}[@]"
          for j in "${!n}"; do
             echo "$i/$j"
          done
      done
      

      作为间接扩展的替代方法,还有一个名称引用

      igneous_rocks=(aa adakite pahoehoe)
      metamorphic_rocks=(eclogite marble)
      sedimentary_rocks=(argillite chalk jaspillite)
      rock_types=(igneous_rocks metamorphic_rocks sedimentary_rocks)
      for i in "${rock_types[@]}"; do
          declare -n n="$i"
          for j in "${n[@]}"; do
             echo "$i/$j"
          done
      done
      

      【讨论】:

      • 我查看了您发布的代码解决方案,并应用它非常有效地创建了.json 文件。我想这就是我以后要使用的方法。
      • 我还建议您使用jq 来编写 JSON 而不是连接字符串。
      • 第一个代码工作得很好,但我有一个小问题。我将所有变量从小写字母更改为大写字母,现在什么也没有发生。我发现这个代码n="${r//_rocks/_block}" 似乎是问题所在。仅当我保留小写字母 n="${r//_rocks/_BLOCK}" 包括可变小写字母时,它似乎才有效。如何在脚本不执行任何操作的情况下只使用大写字母?
      • 抱歉,我认为没有什么可以使您免于理解代码的实际作用。 shell paramater expansion。如果将其更改为大写,则必须将 rock_types 的值更改为大写。或者,您可以使用 ${parameter^^pattern} 扩展将其大写。除此之外,请出示代码。
      【解决方案4】:

      而不是使用 bash/shell 生成您的客户端数据文件,因为它不是很便携且难以维护,因为它是另一个框架和工具集,可以添加到您的构建环境中;

      您最好使用 Minecraft-Forge 项目在此处记录的专用 Gradle 任务实现数据生成器:https://mcforge.readthedocs.io/en/1.14.x/datagen/intro/

      如果你仍然真的想用 bash 来做这件事。如果您使用 nameref 变量来动态引用相应的岩石类型数组,您的代码就可以工作:

      #!/usr/bin/env bash
      
      # shellcheck disable=SC2034 # nameref used
      igneous_rocks=('aa' 'adakite' 'pahoehoe')
      # shellcheck disable=SC2034 # nameref used
      metamorphic_rocks=('eclogite' 'marble')
      # shellcheck disable=SC2034 # nameref used
      sedimentary_rocks=('argillite' 'chalk' 'jaspillite')
      rock_types=('igneous_rocks' 'metamorphic_rocks' 'sedimentary_rocks')
      
      #Buttons
      declare -n rock_type
      for rock_type in "${rock_types[@]}"; do
        for rock_name in "${rock_type[@]}"; do
          cat <<JSON >"${rock_name}_button.json"
      {
        "parent": "block/button",
        "textures": {
          "texture": "strata:blocks/${!rock_type}/$rock_name"
        }
      }
      JSON
        done
      done
      

      生成adakite_button.json的示例:

      {
        "parent": "block/button",
        "textures": {
          "texture": "strata:blocks/igneous_rocks/adakite"
        }
      }
      

      或者使用jq 来构建JSON 而不是here_document:

      #!/usr/bin/env bash
      
      # shellcheck disable=SC2034 # nameref used
      igneous_rocks=('aa' 'adakite' 'pahoehoe')
      # shellcheck disable=SC2034 # nameref used
      metamorphic_rocks=('eclogite' 'marble')
      # shellcheck disable=SC2034 # nameref used
      sedimentary_rocks=('argillite' 'chalk' 'jaspillite')
      rock_types=('igneous_rocks' 'metamorphic_rocks' 'sedimentary_rocks')
      
      #Buttons
      declare -n rock_type
      for rock_type in "${rock_types[@]}"; do
        for rock_name in "${rock_type[@]}"; do
          jq -n --arg rocktype "${!rock_type}" --arg rockname "$rock_name" \
            '{"parent":"block/button","textures":{"texture":("strata:blocks/"+$rocktype+"/"+$rockname)}}' >"${rock_name}_button.json"
        done
      done
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-11
        • 1970-01-01
        • 2020-05-22
        • 1970-01-01
        • 2016-12-23
        • 1970-01-01
        • 2014-03-05
        • 2020-05-22
        相关资源
        最近更新 更多