0

Assume I've got these variables in a bash script:

path_family="/home/family"
path_family_log="/var/log/family.log"
path_friends="/home/friends"
path_friends_log="/var/log/friends.log"
path_pets="/home/pets"
path_pets_log="/var/log/pets.log"

I want to create a for loop where I could do something like the following:

for TYPE in family friends pets
do
  for FILE in $path_<TYPE>
  do
    cat $FILE >> $path_<TYPE>_log
  done
done

Obviously this isn't correct code, just most direct way to express what I want. I'm pulling my hair out trying to figure out how to do the substring substitution on the variable name and have it work as intended.

TMC
  • 111
  • Associative arrays like in https://unix.stackexchange.com/a/452760/70524 might be the best option – muru Jul 22 '19 at 04:15
  • @muru I've seen that thread but can't figure out the substring part whereas that example is a full variable name – TMC Jul 22 '19 at 04:32
  • TYPE=family; var=path_$TYPE; echo ${!var}. But arrays would be better. – muru Jul 22 '19 at 05:01

2 Answers2

2

I'd suggest using associative arrays instead:

#! /bin/bash -
typeset -A dir log # declare both variables as associative arrays
dir=(
   [family]=/home/family
  [friends]=/home/friends
     [pets]=/home/pets
)
log=(
   [family]=/var/log/family.log
  [friends]=/var/log/friends.log
     [pets]=/var/log/pets.log
)
for type in "${!dir[@]}"
do
  cat -- "${dir[$type]}/somefile" >> "${log[$type]}"
done

("${!array[@]}" being the ksh syntax to retrieve the list of keys of an array (in no particular order)).

Or more legibly in zsh (which has had associative arrays decades before bash):

#! /bin/zsh -
typeset -A dir log
dir=(
  family  /home/family
  friends /home/friends
  pets    /home/pets
)
log=(
  family  /var/log/family.log
  friends /var/log/friends.log
  pets    /var/log/pets.log
)
for type in ${(k)dir}
do
  cat -- $dir[$type]/somefile >> $log[$type]
done

With ksh93 (from which bash borrowed its associative array syntax), you could also use associative arrays of compound variables:

#! /bin/ksh93 -
conf=(
   [family]=(dir=/home/family;  log=/var/log/family.log)
  [friends]=(dir=/home/friends; log=/var/log/friends.log)
     [pets]=(dir=/home/pets;    log=/var/log/pets.log)
)
for type in "${!conf[@]}"
do
  cat -- "${conf[$type].dir}/somefile" >> "${conf[$type].log}"
done
  • What does typeset -A dir log do in this? – TMC Jul 22 '19 at 06:47
  • @TMC it makes associative arrays – muru Jul 22 '19 at 07:00
  • can you explain when you use ! when referencing an item in the array vs when you don't? e.g. in the for loop declaration you use "${!dir[@]}" – TMC Jul 22 '19 at 07:09
  • @TMC, "${!a[@]}" is the ksh syntax to retrieve the list of keys of an array, equivalent to zsh's k parameter expansion flag (${(k)a}). See edit. – Stéphane Chazelas Jul 22 '19 at 07:52
  • @StéphaneChazelas Thank you! this is very helpful. Another question. When do I need to surround a reference to the associative array with double quotes or not? is it if I want expansion of a variable first before evaluation of the array? e.g. "${dir[$type]}" vs ${dir[family]} is correct use of double quotes? – TMC Jul 22 '19 at 19:03
0

Keeping in mind of your provided variables and below code will achieve what you're looking for. I hope this may help you and other now and in future.

Bash Multiline:

for TYPE in family friends pets 
do  
    for FILE in $(eval echo "\$path_$TYPE/*")
    do 
        cat $FILE >> $(eval echo "\$path_${TYPE}_log")
    done
done

Single Liner

for TYPE in family friends pets ;do  for FILE in $(eval echo "\$path_$TYPE/*"); do cat $FILE >> $(eval echo "\$path_${TYPE}_log") ;done ; done
muru
  • 72,889
Mahesh K.
  • 1,646