0
# SETUP PHP81 SYMLINKS
declare -A cgi=([path]="/opt/remi/php81/root/usr/bin/php-cgi" [filename]="php-cgi81")
declare -A config=([path]="/opt/remi/php81/root/usr/bin/php-config" [filename]="php-config81")
declare -A phpize=([path]="/opt/remi/php81/root/usr/bin/phpize" [filename]="phpize81")
declare -A pecl=([path]="/opt/remi/php81/root/usr/bin/pecl" [filename]="pecl81")
declare -A pear=([path]="/opt/remi/php81/root/usr/bin/pear" [filename]="pear81")

declare -a symlinks=("cgi" "config" "phpize" "pecl" "pear")

echo -en "[INFO]: Setting up PHP 8.1 symlinks\n"
cd /usr/bin

for symlink in "${symlinks[@]}";
do
    echo -en "[INFO]: Creating symlink for ${symlink}\n"

    # Get the array that matches the string in symlink
    props="$symlink[@]"
    # Print out the filename property eg cgi[filename], config[filename], phpize[filename]
    echo ${!props[filename]}




    echo -en "\n"

done

There is the section of code giving me issues. It currently outputs

./upgrade.sh 
[INFO]: Installing php81 packages
[INFO]: Setting up PHP 8.1 symlinks
[INFO]: Creating symlink for cgi
php-cgi81 /opt/remi/php81/root/usr/bin/php-cgi

[INFO]: Creating symlink for config
php-config81 /opt/remi/php81/root/usr/bin/php-config

[INFO]: Creating symlink for phpize
phpize81 /opt/remi/php81/root/usr/bin/phpize

[INFO]: Creating symlink for pecl
pecl81 /opt/remi/php81/root/usr/bin/pecl

[INFO]: Creating symlink for pear
pear81 /opt/remi/php81/root/usr/bin/pear

The expected output is 
./upgrade.sh 
[INFO]: Installing php81 packages
[INFO]: Setting up PHP 8.1 symlinks
[INFO]: Creating symlink for cgi
php-cgi8=

[INFO]: Creating symlink for config
php-config81

[INFO]: Creating symlink for phpize
phpize81

[INFO]: Creating symlink for pecl
pecl81

[INFO]: Creating symlink for pear
pear81

This is now solved I posted the solution below but someone down voted it because they dislike my solution but the solution I posted is working. But you decide what you want to use. Thanks for the help.

7
  • You're trying to save something like config[@] in props, and then use the indirect expansion to index into config through props? IIRC that doesn't work, ${!p[idx]} indexes into p first (treating idx as an arithmetic expression, so if it ends up resolving to strings that don't name an existing variable, it evaluates as zero, giving you p[0] , or just p, given how scalars and the zeroth element of arrays are the same thing)
    – ilkkachu
    Commented Jul 5, 2022 at 16:21
  • I think it works better through a nameref, e.g. declare -n foo=config; echo "${foo[filename]}". I also think there's an answer about this on the site, but I didn't test now and don't have the time to search...
    – ilkkachu
    Commented Jul 5, 2022 at 16:22
  • declare -n foo=config; echo "${foo[filename]}" How would I use the corresponding array from the main loop in this example? Also when trying to use declare -n I get ./upgrade.sh: line 33: declare: -n: invalid option Commented Jul 5, 2022 at 16:26
  • What bash version are you using? Commented Jul 5, 2022 at 17:14
  • 1
    Instead of having 5 associative arrays (cgi, config, phpize, pecl, pear), each with path and filename keys, you would be better off having just two associative arrays (AKA "hashes") named paths and filenames, each with keys cgi, config, phpize, pecl, and pear. Variable indirection is almost always a bad idea (and avoiding it is one the reasons hashes are useful). then you could just iterate over the symlinks array, which contains the keys for both hashes.
    – cas
    Commented Jul 6, 2022 at 8:58

4 Answers 4

2

You'll need to create an indirect variable for each index in the associative array:

for symlink in "${symlinks[@]}";
do
    echo -en "[INFO]: Creating symlink for ${symlink}\n"

    f_var="${symlink}[filename]"
    p_var="${symlink}[path]"

    echo "filename=${!f_var}  path=${!p_var}"
done

If you want to get more convoluted, make cgi, etc to be indexed arrays, and an associative array can hold the index of the particular property:

i=0; declare -A idx=([path]=$((i++)) [filename]=$((i++)))
unset cgi; declare -a cgi=( "/opt/remi/php81/root/usr/bin/php-cgi" "php-cgi81")
symlink=cgi
props="${symlink}[@]"
values=("${!props}")
echo ${values[${idx[path]}]}     # => /opt/remi/php81/root/usr/bin/php-cgi
echo ${values[${idx[filename]}]} # => php-cgi81

Clearly declare -n symlink=cgi is much tidier, if you can upgrade.

2

Instead of having 5 associative arrays (cgi, config, phpize, pecl, and pear), each with path and filename keys, you would be better off having just two associative arrays named paths and filenames, each with keys for cgi, config, phpize, pecl, and pear.

Then you could just iterate over the symlinks array, which contains the keys for both hashes.

For example:

#!/bin/bash

declare -A paths=(
  [cgi]=/opt/remi/php81/root/usr/bin/php-cgi
  [config]=/opt/remi/php81/root/usr/bin/php-config
  [phpize]=/opt/remi/php81/root/usr/bin/phpize
  [pecl]=/opt/remi/php81/root/usr/bin/pecl
  [pear]=/opt/remi/php81/root/usr/bin/pear
)

declare -A filenames=(
  [cgi]=php-cgi81
  [config]=php-config81
  [phpize]=phpize81
  [pecl]=pecl81
  [pear]=pear81
)

declare -a symlinks=(cgi config phpize pecl pear)

fmt='%-6s\t%-39s\t%s\n'
printf "$fmt" "KEY" "PATH" "FILENAME"
for s in "${symlinks[@]}"; do
  printf "$fmt" "$s" "${paths[$s]}" "${filenames[$s]}"
done

Sample output:

KEY     PATH                                    FILENAME  
cgi     /opt/remi/php81/root/usr/bin/php-cgi    php-cgi81 
config  /opt/remi/php81/root/usr/bin/php-config php-config81
phpize  /opt/remi/php81/root/usr/bin/phpize     phpize81  
pecl    /opt/remi/php81/root/usr/bin/pecl       pecl81    
pear    /opt/remi/php81/root/usr/bin/pear       pear81    
1

If switching to ksh93 is an option:

#! /bin/ksh93 -
conf=(
  [cgi]=(
    path=/opt/remi/php81/root/usr/bin/php-cgi
    filename=php-cgi81
  )
  [config]=(
    path=/opt/remi/php81/root/usr/bin/php-config 
    filename=php-config81
  )
  [phpize]=(
    path=/opt/remi/php81/root/usr/bin/phpize
    filename=phpize81
  )
  [pecl]=(
    path=/opt/remi/php81/root/usr/bin/pecl
    filename=pecl81
  )
  [pear]=(
    path=/opt/remi/php81/root/usr/bin/pear
    filename=pear81
  )
)

print "[INFO]: Setting up PHP 8.1 symlinks"
cd /usr/bin || exit

for symlink in "${!conf[@]}"; do
    print -r "[INFO]: Creating symlink for ${symlink}"

    print -r ln -s -- "${conf[$symlink].path}" "${conf[$symlink].filename}"
done

Would give something like:

[INFO]: Setting up PHP 8.1 symlinks
[INFO]: Creating symlink for cgi
ln -s -- /opt/remi/php81/root/usr/bin/php-cgi php-cgi81
[INFO]: Creating symlink for config
ln -s -- /opt/remi/php81/root/usr/bin/php-config php-config81
[INFO]: Creating symlink for pear
ln -s -- /opt/remi/php81/root/usr/bin/pear pear81
[INFO]: Creating symlink for pecl
ln -s -- /opt/remi/php81/root/usr/bin/pecl pecl81
[INFO]: Creating symlink for phpize
ln -s -- /opt/remi/php81/root/usr/bin/phpize phpize81

Though here, It seems you should be able to do just:

#! /bin/sh -
version=81
srcdir=/opt/remi/php$version/root/usr/bin
dstdir=/usr/bin
info() { printf '[INFO] %s\n' "$1"; }

info "Setting up PHP $version symlinks"
for file in php-cgi php-config pear pecl phpize; do
  info "Creating symlink for $file"
  ln -s -- "$srcdir/$file" "$dstdir/$file$version" || exit
done

Without having to repeat yourself that much and remove the dependency to bash or ksh93.

-1

I have found a the answer

# SETUP PHP81 SYMLINKS
declare -A cgi=([path]="/opt/remi/php81/root/usr/bin/php-cgi" [name]="php-cgi81")
declare -A config=([path]="/opt/remi/php81/root/usr/bin/php-config" [name]="php-config81")
declare -A phpize=([path]="/opt/remi/php81/root/usr/bin/phpize" [name]="phpize81")
declare -A pecl=([path]="/opt/remi/php81/root/usr/bin/pecl" [name]="pecl81")
declare -A pear=([path]="/opt/remi/php81/root/usr/bin/pear" [name]="pear81")

declare -a files=("cgi" "config" "phpize" "pecl" "pear")

cd /usr/bin

# Adding links to new binaries
echo "[INFO]: Setting up PHP 8.1 symlinks"
for file in ${files[*]};
do
  echo "[INFO]: Creating symlink for ${file}"
  link_name="$file[name]"
  link_path="$file[path]"
  if [ ! -f "${!link_name}" ];
  then
    ln -s ${!link_path} ${!link_name}
  else
    echo "[INFO]: Symlink to ${!link_name} already exists skipping this step!"
  fi
done
4
  • isn't that what @glennjackman already posted half an hour before?
    – ilkkachu
    Commented Jul 5, 2022 at 19:04
  • 1
    (what I don't understand is why you'd switch to using ${array[*]} instead of "${array[@]}" when the latter is more correct and you already had that in the original post)
    – ilkkachu
    Commented Jul 5, 2022 at 19:05
  • @ilkkachu Nope its not, doing it his way does not work in this environment the way I posted does. The * got switched when I was trying to find the issue. Commented Jul 6, 2022 at 15:11
  • glenn has for symlink in "${symlinks[@]}"; ... f_var="${symlink}[filename]" there, while you have for file in ${files[*]}; ... link_name="$file[name]" there. Both put the array name and the index in the variable used for indirect access. And then both use that variable in the same way: ${!f_var} and ${!link_name}. Barring the renaming, that looks exactly the same to me.
    – ilkkachu
    Commented Jul 6, 2022 at 15:46

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.