【问题标题】:Bash: reading multiple values from an array by using IFS=':' dynamicallyBash:动态使用 IFS=':' 从数组中读取多个值
【发布时间】:2021-06-09 12:46:14
【问题描述】:

目前,我的主要项目存在于一个配置过程之外,用于构建支持 Docker 环境的部署/生产服务器,我选择在 Bash 中进行。也许这很糟糕,但对我来说挑战很大。我已经为它构建了一个结构,该结构依赖于具有自己任务的文件,以及不同类型的功能,以使其表现为框架类型的部署。我花了很多时间重建它并改进自己,因为这是我真正想要完成的第一个项目,至少我想完成智能行为。

但在过去的几天里,我为我正在写的一个部分考虑了一个解决方案; 任务是确保已创建目录的快捷方式集合并填充 Git 存储库内容,并且我想从那里执行部署。此目的地的这些存储库主要是支持 Docker 映像或 Git 挂钩部署的配置文件。但是,我使用不同类型的数组列表:

DEPLOYMENT_DIRECTORY_ASSIGNMENT=(
  "layer_1:layer_2:server-deployment-repository"
  "layer_1:layer_2:layer_3:deployment-hooks-repository"
  "layer_1:layer_2:layer_3:nginx-configs-repository"
)

一些解释; 'layers_*' 应该是目录,这些将是 在服务器环境中创建为格式:'layer_1/layer_2/layer_3'。我的计划 是使用layer_1作为隐藏目录意味着它们最多可以相等, layer_2 也可以相等,并将用作一个类别。但是,“layer_3”不是 总是需要的(例如数组的第一个值)。对于第一个条目 'layer_1:layer_2:server-deployment-repository' 我将使用它作为 '.hidden/setup' 因为我 希望能够从那里(重新)启动部署,并且 根据其格式,似乎没有理由使用第三层。

列表末尾从左到右的值显然是存储库名称并用作源。这不是像其他目录层那样的目录层,但它将用于根据特定路径查找存储库,并提供配置存储库内容和分层文件夹之间关系的功能。

我目前正在完成这项任务,但解决方案并不总是最干净的。通过该说明,我使用以下内容来实现这一点(主要不是根据上面的数组):

for collection in ${DEPLOYMENT_DIRECTORY_ASSIGNMENT[@]}; do
        
        while IFS=':' read -r first_layer second_layer third_layer repository
        do
    
            first_layers+=($first_layer)
            second_layers+=($second_layer)
            third_layer+=($third_layer)
            source_repositories+=($repository)

            u_layers=($(echo "${layers[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
    
        done <<< "$collection"

    done

这至少在某种程度上是静态的,但是我将每个值添加到数组中并稍后执行几个 if-else 检查,以在将层创建为目录时保持所有内容的排序。对于彼此相等的层/文件夹,我使用选项对唯一字符进行排序以防止重复问题。

但由于从那时起这是静态的,我想弄清楚以下几点:

当层数不同时,我可以使用“while IFS=':' read -r”的组合来支持动态循环遍历除以':'的值的选项吗?

如:

"layer_1:layer_2:repository"
"layer_1:layer_2:layer_3:repository"
"layer_1:layer_2:layer_3:layer_4:layer_5:layer_6:repository"
"layer_1:layer_2:layer_3:layer_4:layer_5:repository"

我知道 Bash 并不总是最合乎逻辑的选择,但是当我开始这些项目时,我想挑战自己并更多地了解这种语言。

但是什么是最好的解决方案呢?

【问题讨论】:

  • Mucho texto。你不能简单地描述一下你到底想要什么,并提供一个minimal reproducible example吗?
  • Lo siento。我可以写更长的,但我确实更喜欢短。并且喜欢提供一些信息作为背景故事来回答可能的常见问题。不过感谢您的反馈。
  • 与其试图预测所有可能的问题,更好的(和推荐的)策略是使问题简短而切题,只包含传达问题所需的内容,仅此而已。不需要背景故事,事实上,它会分散注意力。

标签: linux bash loops deployment logic


【解决方案1】:

使用read-a 选项将结果写入可变大小数组:

# split collection into array
IFS=':' read -r -a layers <<< "$collection"

# extract the repository from the last array element
source_repositories=${layers[-1]}

# iterate over all array elements but the last one
for layer in ${layers[@]::${#layers[@]}-1}; do
    # do something with each layer
    echo ${layer}
done

【讨论】:

  • 请在您的变量扩展周围添加缺少的双引号。 for layer in "${layers[@]::${#layers[@]}-1}"; do, echo "${layer}"
  • 这应该会有所帮助,谢谢。但是我还看不到如何将这些层创建为目录,变量“层”似乎总是相等的,并且通过检查路径/${层}的存在,然后创建路径/${层,它不起作用}/${layer} 并进一步......如果我是正确的。是否有一种解决方案可以将数字作为计数添加到图层中?这样,如果一切都有意义的话,我将所有内容与 $collection 进行比较以构建路径似乎是合乎逻辑的
  • @Colin 我们无法为您提供帮助,因为您在答案的评论中添加了使命陈述,但您的问题没有明确说明您想要做什么。所以你只会得到部分答案。请关注每个帖子/问题的一个问题。再次查看“帮助”部分的“如何提问”。
  • @Colin:你的问题是:“我可以使用“while IFS=':' read -r”的组合,支持动态循环通过值除以':' 的选项,当有不同的层数?” - 我的帖子正好回答了这个问题。该问题中没有关于创建目录的内容。请一次只问一个问题。
  • Thomas,非常感谢您的帮助。但是,在我的主要问题中,我确实从一开始就提到我想将图层创建为目录。根据对这些层进行分组的功能,这似乎只是一个小补充,这是我的意图。
【解决方案2】:

如果您只是想确保不同层定义的路径存在,以下方法可以解决问题:

for collection in ${DEPLOYMENT_DIRECTORY_ASSIGNMENT[@]}; do
    # assert that all layer directories exist
    layers=$(echo ${collection%:*} | tr : /)
    mkdir -p "${layers}"

    # extract the source repository
    repository=${collection:${#layers}+1}
done

【讨论】:

    【解决方案3】:

    这里有一种方法。

    #!/usr/bin/env bash
    
    DEPLOYMENT_DIRECTORY_ASSIGNMENT=(
      first_line_layer_1:first_line_layer_2:first_line_repository
      second_line_layer_1:second_line_layer_2:second_line_layer_3:second_line_repository
      third_line_layer_1:third_line_layer_2:third_line_layer_3:third_line_layer_4:third_line_layer_5:third_line_layer_6:third_line_repository
      fourth_line_layer_1:fourth_line_layer_2:fourth_line_layer_3:fourth_line_layer_4:fourth_line_layer_5:fourth_line_repository
    )
    
    while IFS=':' read -ra values; do
      total=${#values[*]}
      layers=("${values[@]:0:total - 1}")
      repository=${values[-1]}
      num=1
      for i in "${!layers[@]}"; do
        declare -a "$(printf '%s_%d+=(%s)\n' "Layer" "$num" "${layers[i]}")"
        ((num++))
      done
      declare -a "$(printf '%s+=(%s)\n' "Repository" "$repository")"
    done < <(printf '%s\n' "${DEPLOYMENT_DIRECTORY_ASSIGNMENT[@]}")
    

    用相应的字段/列检查数组的值是什么。

    declare -p Layer_{1..6}
    declare -p Repository
    

    输出

    declare -a Layer_1=([0]="first_line_layer_1" [1]="second_line_layer_1" [2]="third_line_layer_1" [3]="fourth_line_layer_1")
    declare -a Layer_2=([0]="first_line_layer_2" [1]="second_line_layer_2" [2]="third_line_layer_2" [3]="fourth_line_layer_2")
    declare -a Layer_3=([0]="second_line_layer_3" [1]="third_line_layer_3" [2]="fourth_line_layer_3")
    declare -a Layer_4=([0]="third_line_layer_4" [1]="fourth_line_layer_4")
    declare -a Layer_5=([0]="third_line_layer_5" [1]="fourth_line_layer_5")
    declare -a Layer_6=([0]="third_line_layer_6")
    declare -a Repository=([0]="first_line_repository" [1]="second_line_repository" [2]="third_line_repository" [3]="fourth_line_repository")
    

    【讨论】:

    • 这与我的回答中的方法相同(使用read-a 选项来填充数组),只是需要更多代码。
    • @Thomas,感谢您的精彩评论,但除了IFS=':'read -a,我在我们的代码中看不到任何相似之处。 (这是拆分字符串的正确方法)
    • 我已根据 OP 的注释将每列的字符串与一个数组和一个数字增量进行分组,而不是使用 &lt;&lt;&lt; 此处字符串,而是使用 Process Substitution。
    猜你喜欢
    • 2011-05-22
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-23
    • 2022-01-24
    • 1970-01-01
    • 2014-10-27
    相关资源
    最近更新 更多