Bash: 使用 IFS=':' 动态读取数组中的多个值

Bash: reading multiple values from an array by using IFS=':' dynamically

目前我的主要项目存在于构建支持 Docker 环境的 deployment/production 服务器的一个配置过程中,我选择在 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 检查,以便在将层创建为目录时保持所有内容排序。对于彼此相等的layers/folders,我使用选项对唯一字符进行排序以防止重复问题。

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

我可以使用“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 并不总是最合乎逻辑的选择,但是当我开始这些项目时,我想挑战自己并了解更多关于这门语言的知识。

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

使用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

这是一种方法。

#!/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[@]}")

检查对应field/colum的数组的值是多少。

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")

如果您只是想确保由不同层定义的路径存在,则可以使用以下方法:

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