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.

3
  • Associative arrays like in unix.stackexchange.com/a/452760/70524 might be the best option Commented Jul 22, 2019 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 Commented Jul 22, 2019 at 4:32
  • TYPE=family; var=path_$TYPE; echo ${!var}. But arrays would be better. Commented Jul 22, 2019 at 5:01

2 Answers 2

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
5
  • What does typeset -A dir log do in this? Commented Jul 22, 2019 at 6:47
  • @TMC it makes associative arrays Commented Jul 22, 2019 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[@]}" Commented Jul 22, 2019 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. Commented Jul 22, 2019 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? Commented Jul 22, 2019 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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.