3

I'm using rsync to copy specific files from a source directory (and subdirectories) to a destination directory (and subdirectories). The mapping of the subdirectories is not identical, so I'm defining arrays of subdirectories of the destination directory that contain the source file paths.

I've been unable to successfully loop over the destination arrays with access to the names of the arrays. Here's a MWE (filenames and directories must be edited, obviously).

#!/bin/bash

sourcedir=~/Dropbox/230/
destdir=~/Dropbox/230/website/

# Destination arrays
organization=(
syllabus/syllabus.pdf
)

notes=(
notes/ME230_2014S_Part0_Lec00.pdf
notes/ME230_2014S_Part1_Lec01_partial.pdf
)

echo "synchronizing: rsyncing from $sourcedir to $destdir"


for destsubdir in $organization $notes
do
  for sourcesubdir in "${destsubdir[@]}"
  do
    echo "from $sourcedir$sourcesubdir to $destdir$destsubdir/"
    rsync -avz "$sourcedir$sourcesubdir" "$destdir$destsubdir/"
  done
done

This is obviously wrong because I don't want the destination to be the contents of $destsubdir but the name of the variable itself. I've been unable to successfully achieve this.

Note that a proper solution would allow any number of arrays to included without restructuring (e.g. no extra for-loops).

If multi-indexed arrays were possible in bash, this would be much easier, I think. Thanks for any help!

Solution

@jeanrjc has a good solution. Here's what it looks like in terms of my example above.

#!/bin/bash

sourcedir=~/Dropbox/230/
destdir=~/Dropbox/230/website/

# Destination arrays
organization=(
  syllabus/syllabus.pdf
)

notes=(
  notes/ME230_2014S_Part0_Lec00.pdf
  notes/ME230_2014S_Part1_Lec01_partial.pdf
)

destsubdirArray=(
  organization
  notes
)

echo "synchronizing: rsyncing from $sourcedir to $destdir"

for destsubdir in ${destsubdirArray[*]}
do
  destsubdirContents=$destsubdir[@];
  for sourcesubdir in ${!destsubdirContents}
    do
    echo "from $sourcedir$sourcesubdir to $destdir$destsubdir/"
    rsync -avz "$sourcedir$sourcesubdir" "$destdir$destsubdir/"
  done
done
4
  • Can you show us what you expect from the last echo ? Commented Apr 3, 2014 at 14:32
  • @jeanrjc I would like to see, for instance, from Users/picone/Dropbox/230/syllabus/syllabus.pdf to Users/picone/Dropbox/230/website/organization/. Commented Apr 3, 2014 at 17:38
  • hmmm, I don't get it. why Users/picone/Dropbox/230/website/organization/ ? Can you just re-write the question with simple example, and a clear explanation of the expected behavior ? Did you check my answer btw? does it help ? Commented Apr 3, 2014 at 18:29
  • @jeanrjc I think your answer works, but I haven't checked it yet. It appears to be correct, given the output you've shown. I'm evaluating dave-sines answer because it's an interesting trick. However, I think yours is probably the most kosher. I'll probably mark it as the accepted answer once I test it. Thanks for the help! Commented Apr 3, 2014 at 18:59

3 Answers 3

4

I'm not sure I get what you meant, but maybe this can help you :

one=( "one" 5 6 7 8 )
two=( "two" 11 22 33 )
three=( "tree" 777 888 999 )
all=( one two three )

for i in ${all[*]}; do 
  ref=$i[@]; 
  for j in ${!ref}; do 
      echo $j
   done
done

one
5
6
7
8
two
11
22
33
tree
777
888
999
Sign up to request clarification or add additional context in comments.

Comments

1

Bash 4.3 supports namerefs -- the expansion of a nameref is the expansion of the nameref's contents rather than the nameref itself (it may help to think of namerefs as being similar to symlinks).

declare -n destsubdir

for destsubdir in organization notes ; do
  for sourcesubdir in "${destsubdir[@]}" ; do
    echo "$sourcedir$sourcesubdir $destdir$sourcesubdir"
  # rsync -avz "$sourcedir$sourcesubdir" "$destdir$sourcesubdir"
  done
done

unset -n destsubdir

For earlier bash versions the nearest equivalent is, as kisp states, to eval the entire inner loop. The quoting requirements can get out of hand really quickly.

for destsubdir in organization notes ; do
  eval 'for sourcesubdir in "${'"$destsubdir"'[@]}" ; do
          echo "$sourcedir$sourcesubdir" "$destdir$sourcesubdir"
  #        rsync -avz "$sourcedir$sourcesubdir" "$destdir$sourcesubdir"
        done'
done

Note: those rsync commands have not been tested.

1 Comment

Your answer is intriguing. I tried to update bash to 4.3, but brew only took me to 4.2.45, which doesn't recognize namerefs.
0

Solution to pass the variable names instead of the variables :

You can pass the variable names itself to the first for, then evaluate them in the second if needed.

like

for var_name in variable1 variable2 ; do
eval "myvar=\$$var_name"

Anyway, the single for loop can be used with the elements only if you use ${array[@]} everywhere.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.