0

I have 15 paths stored in a array variable, and file names, also stored as another array variable and I want to bind each path to its corresponding file. I was looking for sth that will bind each path element with its corresponding file like this:

ar1=([1]="path1" [2]="path2")
ar2=([1]="file1" [2]="file2")

and I need:

ar3=([1]="path1/file1" [2]="path2/file2")

I successfully managed w/ a simple for-loop:

for i in {1..2}; do
  ar3=("${ar1[$i]}""/""${ar1[$i]}"".txt")
done

but I need the variable ar3 for further parts of the script and I cannot use it outside the for-loop sub-shell.

Thanks a lot, Guy

Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
Guy
  • 3,535
  • 2
  • 12
  • 9
  • 1
    for loops do not introduce a sub-shell context. You don't need to drop in and out of quotes like that. You also don't need to use `$i` in `[$i]`, array indexing is an expression context so `[i]` is enough. – Etan Reisner Oct 29 '14 at 23:03
  • @JohnKugelman Because I don't actually know what his question *is* and my answer is just a negation of a supposition inherent in the unasked question. That might very well be the answer if the question is actually "will this work because I'm afraid a sub-shell will get in the way" in which case the answer is "no it won't". – Etan Reisner Oct 29 '14 at 23:06
  • can you write how will you do the loop; when I put an echo ${ar3[*]} after the loop it doesn't print it. – Guy Oct 29 '14 at 23:10
  • 1
    @Guy, the for loop itself won't stop that from working, so you're doing something somewhere else in your code (like implicitly creating a subshell) that's breaking it. Because you aren't showing us any other code, we can't tell you where/how you're doing it wrong. – Charles Duffy Oct 30 '14 at 00:02
  • 1
    BTW, `echo ${ar3[*]}` is generally bad practice -- you have no way to tell the difference between `ar3=( "foo bar" baz )` and `ar3=( foo "bar baz" )` with that usage, and `ar3=( "*" )` will _really_ mess you up (printing a list of files in the current directory, rather than a `*` at all). – Charles Duffy Oct 30 '14 at 00:10

1 Answers1

1

You can test that this works, and propagates the results beyond the loop, like so:

dirnames=( path1 path2 )
basenames=( file1 file2 )
paths=( )
for idx in "${!dirnames[@]}"; do
  [[ ${basenames[$idx]} ]] || continue
  paths[$idx]=${dirnames[$idx]}/${basenames[$idx]}.txt
done
printf '%q\n' "${paths[@]}"

You'll see that this does properly print path1/file1 and path2/file2.

There are several improvements here -- but the only critical thing it does is initialize the array before the loop. Not doing this would mean that only the last item being iterated over would be stored, as you'd be overwriting the array with a new (single) item on each iteration.

If you're seeing prior behavior of returning zero items, rather than one item, you most likely have a subshell created in a part of your code not given in your question. See BashFAQ #24 for discussion of this common issue.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Although the code is excellent (apart, perhaps, from the missing '.txt' suffix) it is not correct to say that it does nothing significantly different from the code in the question. First, it appends basename to dirname instead of dirname to dirname. Second, it sets the appropriate index in the paths array every time round the loop instead of re-initializing the ar3 array to a single-element array every time around the loop. – pjh Oct 30 '14 at 00:38
  • @pjh, ...heh -- some good points; I apparently didn't read the original closely enough. Well, I'm happy to provide an example of how to do it right (and edit in the extension). – Charles Duffy Oct 30 '14 at 00:41