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.

marked as duplicate by muru, Kusalananda bash Jul 22 at 6:42

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • Associative arrays like in unix.stackexchange.com/a/452760/70524 might be the best option – muru Jul 22 at 4: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 at 4:32
  • TYPE=family; var=path_$TYPE; echo ${!var}. But arrays would be better. – muru Jul 22 at 5:01
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 at 6:47
  • @TMC it makes associative arrays – muru Jul 22 at 7: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 at 7: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 at 7: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 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

Not the answer you're looking for? Browse other questions tagged or ask your own question.