Cannot iterate associative array keys in PKGBUILD

87 views Asked by At

I am working with a PKGBUILD file for the AUR. I have a lot of colors that need to be replaced in different files in the $pkgsrc directory and I wanted to use an associative array.

declare -A _BLACKISH_REPLACEMENTS
_BLACKISH_REPLACEMENTS['#242424']='#1C1C1C'
_BLACKISH_REPLACEMENTS['#333333']='#292929'
_BLACKISH_REPLACEMENTS['#999999']='#787878'
_BLACKISH_REPLACEMENTS['#555555']='#4C4C4C'
_BLACKISH_REPLACEMENTS['#373737']='#2E2E2E'
_BLACKISH_REPLACEMENTS['#434343']='#383838'
_BLACKISH_REPLACEMENTS['#3E3E3E']='#333333'
_BLACKISH_REPLACEMENTS['#383838']='#2E2E2E'
_BLACKISH_REPLACEMENTS['#313131']='#262626'
_BLACKISH_REPLACEMENTS['#101010']='#101010'
_BLACKISH_REPLACEMENTS['#3B3B3B']='#303030'
_BLACKISH_REPLACEMENTS['#2A2A2A']='#1F1F1F'
_BLACKISH_REPLACEMENTS['#656565']='#575757'
_BLACKISH_REPLACEMENTS['#767676']='#5E5E5E'
_BLACKISH_REPLACEMENTS['#868686']='#787878'
_BLACKISH_REPLACEMENTS['#636363']='#595959'
_BLACKISH_REPLACEMENTS['#696969']='#5E5E5E'
_BLACKISH_REPLACEMENTS['#707070']='#666666'
_BLACKISH_REPLACEMENTS['#767676']='#6B6B6B'
_BLACKISH_REPLACEMENTS['#C1C1C1']='#B8B8B8'
_BLACKISH_REPLACEMENTS['#C6C6C6']='#BDBDBD'

That seems like a fairly clean solution, otherwise I would have many variables and that is less than ideal. Now, I iterate over these with the syntax found in other SO posts:

_blackish_replace() (
    shopt -s globstar

    echo "${!_BLACKISH_REPLACEMENTS[@]}"
    echo "${_BLACKISH_REPLACEMENTS[@]}"

    for file in "$1"/**/*.scss; do
        echo "Replacing colors in file: $file"
        for color in "${!_BLACKISH_REPLACEMENTS[@]}"; do
            echo "$color"
            sed -i "s;$color;${_BLACKISH_REPLACEMENTS["$color"]};gI" "$file"
        done
    done
)

It looks good to me, and when this is run in a standalone script, it does indeed replace the correct matches in the correct files.

However, when using it from makepkg, it fails silently, hence the four echo calls exhibited.

The first two output newlines. This leads me to believe they are undefined?

The iteration has proved to be working for the glob expansion, however echo "$color" is never reached; the loop iterates nothing.

I thought maybe makepkg was using the system shell, which in that case, running the code directly from my user shell zsh fails with event not found: _BLACKISH_REPLACEMENTS or something alike (off the top of my head).

I asked in the Arch Linux Discord server if makepkg uses the locally available bash, and was assured it does. I am very confused.

1

There are 1 answers

5
Léa Gris On

It is probably a good idea to turn your array into a sed script before iterating the files:

#!/usr/bin/env bash

declare -A _BLACKISH_REPLACEMENTS=(
  ['#242424']='#1C1C1C'
  ['#333333']='#292929'
  ['#999999']='#787878'
  ['#555555']='#4C4C4C'
  ['#373737']='#2E2E2E'
  ['#434343']='#383838'
  ['#3E3E3E']='#333333'
  ['#383838']='#2E2E2E'
  ['#313131']='#262626'
  ['#101010']='#101010'
  ['#3B3B3B']='#303030'
  ['#2A2A2A']='#1F1F1F'
  ['#656565']='#575757'
  ['#767676']='#5E5E5E'
  ['#868686']='#787878'
  ['#636363']='#595959'
  ['#696969']='#5E5E5E'
  ['#707070']='#666666'
  ['#767676']='#6B6B6B'
  ['#C1C1C1']='#B8B8B8'
  ['#C6C6C6']='#BDBDBD'
)

sed_script=
for k in "${!_BLACKISH_REPLACEMENTS[@]}"; do
  v="${_BLACKISH_REPLACEMENTS[$k]}"
  sed_script+="s/$k/$v/g;"
done

shopt -s globstar nullglob
for file in "$1"/**/*.scss; do
  sed -i.bak -e "$sed_script" "$file"
done

Now in a more practical one-liner POSIX-shell friendly call:

find ./ -type f -name '*.scss' -exec sed -i.bak -e 's/#242424/#1C1C1C/g;s/#696969/#5E5E5E/g;s/#555555/#4C4C4C/g;s/#767676/#6B6B6B/g;s/#868686/#787878/g;s/#383838/#2E2E2E/g;s/#636363/#595959/g;s/#101010/#101010/g;s/#373737/#2E2E2E/g;s/#C6C6C6/#BDBDBD/g;s/#313131/#262626/g;s/#333333/#292929/g;s/#C1C1C1/#B8B8B8/g;s/#707070/#666666/g;s/#434343/#383838/g;s/#3E3E3E/#333333/g;s/#3B3B3B/#303030/g;s/#999999/#787878/g;s/#656565/#575757/g;s/#2A2A2A/#1F1F1F/g;' {} \;

To clarify the point of all the above: As you are unsure about the shell brand running your makepkg, it is a safe route to choose the most portable shell code by sticking to POSIX-shell grammar, common tools and options.

Instead of choosing a quite over-engineered associative array here. The replacement instructions for sed can be layed-out as clearly as your associative array:

#!/usr/bin/env sh

# A plain string of sed replacement instructions
# is as compact and more portable than an associative array.
# It also saves from looping over each entry.

_BLACKISH_REPLACEMENTS='
s/#242424/#1C1C1C/g;
s/#696969/#5E5E5E/g;
s/#555555/#4C4C4C/g;
s/#767676/#6B6B6B/g;
s/#868686/#787878/g;
s/#383838/#2E2E2E/g;
s/#636363/#595959/g;
s/#101010/#101010/g;
s/#373737/#2E2E2E/g;
s/#C6C6C6/#BDBDBD/g;
s/#313131/#262626/g;
s/#333333/#292929/g;
s/#C1C1C1/#B8B8B8/g;
s/#707070/#666666/g;
s/#434343/#383838/g;
s/#3E3E3E/#333333/g;
s/#3B3B3B/#303030/g;
s/#999999/#787878/g;
s/#656565/#575757/g;
s/#2A2A2A/#1F1F1F/g;
'

_blackish_replace() {
  # Instead of iterating a bash specific globstar,
  # find -exec can replace it while sticking to the most
  # genuine POSIX-shell grammar.
  # find and sed tools are used with their most common options
  # avoiding gnu-specific extensions.
  find "$1" -type f -name '*.scss' -exec \
    sed -i.bak -e "$_BLACKISH_REPLACEMENTS" {} \;
}