skip to Main Content

Currently my main project(s) exist out of one configuration process to build a deployment/production server supporting Docker environments, and I’ve chosen to do it in Bash.. maybe that’s bad, but challenging a lot for me. I have build a structure for it that depends on files that have their own tasks, and different sorts of functionalities to let it behave as a framework kind of deployment. I spent many times on rebuilding it and improving myself as it was my real first project that I wanted to finish at least with intelligence behavior.

But the last couple of days, I thought about a solution for one part that I’m writing;
The task is to make sure that a shortcut collection of directories has been created and filled with Git repository content, and I want to execute the deployment from there. These repositories for this destination are mainly configuration files supporting Docker images or Git hook deployment. However, I work with different kinds of array lists:

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

Some explanation; the ‘layers_*’ are supposed to be directories, these are going to be
created within the server environment as the format: ‘layer_1/layer_2/layer_3’. My plan
is to use layer_1 as hidden directories what means that they can be equal at most,
layer_2 can be equal as well and will function as a category. However, ‘layer_3’ isn’t
always needed (example is the first value of the array). For the first entry
‘layer_1:layer_2:server-deployment-repository’ I will use it as ‘.hidden/setup’ since I
want to have the ability for being able to (re) launch the deployment from there, and
according to its format, there seems no reason to use a third layer.

The values at the end of the list from left to right are obviously repository names and serve as sources. This is not a directory layer like the rest, but this will be used to find the repository based from a certain path and to serve the functionality of configuring relations between the repository content and layered folders.

This task is currently working on my end but with solutions that aren’t always the cleanest. By that note, I use the following to make this happen (mostly not according to the array above):

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

This is somewhat static at least, but I add every value to arrays and execute several if-else checks later on for keeping everything sorted when creating layers as directories. For the layers/folders that are equal to each other, I use the option to sort for unique characters to prevent repeating issues.

But because of the fact that this is static from that point, I want to figure out the following:

Can I use a combination of "while IFS=’:’ read -r" supporting the option of dynamically looping through values divided by a ‘:’ when there are different amounts of layers?

Such as:

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

I know that Bash isn’t always the most logical choice but when I started these projects, I wanted to challenge myself and have more knowledge about this language.

But what would be the best solution for this?

3

Answers


  1. Use the -a option of read to write the result into a variable-size array:

    # 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
    
    Login or Signup to reply.
  2. Here one approach.

    #!/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 '%sn' "${DEPLOYMENT_DIRECTORY_ASSIGNMENT[@]}")
    

    To check what is the values of the array with the corresponding field/colum.

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

    Output

    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")
    
    Login or Signup to reply.
  3. In case you simply want to assure that the path defined by the different layers exists, the following would do the trick:

    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
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search