1

I am trying to initialize read an array by splitting a variable.

x=abc:def:gh
declare -a xa
# I want xa[0] = abc, xa[1] = def, and so on

IFS=: read -a xa <<< $x
echo ${#xa[@]} $xa ######### the above did not work
1 abc def gh

# but type the same value from console?
IFS=: read -a xa
abc:def:gh ########## this is what I typed in
echo ${#xa[@]} $xa ######### this worked
3 abc

How do put IFS to work when reading in a variable using <<< ?

Will appreciate your suggestions.

Also, here is my actual problem, just in case there are smarter solutions to it. I use SVN and different people are interested in knowing about different set of paths. In SVN post-commit, I want to filter the list of changes and raise an email to different groups of people according to their desires. So I thought I would set up something like below in hooks-env

NOTIFY_LIST=mailinglist:grep options:grep options:......

and then, in post-commit, parse the svnlook data to see if there was any candidate email. Is there a declarative way to say that a change in such and such paths are of interest to such and such lists of people?

Thanks Dinesh

edit: tried combination of IFS and simply xa=($x). So it appears IFS=: cannot be combined profitably with read. So I have a way to get my job done, but still curious what's happening?

IFS=: xa=($x) # the array is populated as expected
IFS=b xa=($x) # the array is populated as expected

Thanks again.

Lazy Badger
  • 94,711
  • 9
  • 78
  • 110
Dinesh
  • 4,437
  • 5
  • 40
  • 77
  • 1
    There were some bugs in earlier versions of `bash` when using `IFS` like this with here strings, at least some of which have been fixed in 4.3 See http://stackoverflow.com/q/20144593/1126841. – chepner Aug 14 '14 at 21:31

2 Answers2

5

Use more quotes!

IFS=: read -r -a xa <<<"$x"

...works as you expect.

Similarly, if you want to see the contents of an array accurately, echo is the wrong tool, especially when not quoting its arguments. Consider instead:

printf '%q\n' "${xa[@]}"
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • 2
    There was a bug in pre-4.3 versions of `bash`, since assuming the default value of `IFS`, the same string should be seen by `read` whether or not you quote `$x`. Without quotes, `$x` is incorrectly split into three words, but still treated as a single word by `read` when populating the array. – chepner Aug 14 '14 at 21:34
  • @chepner, thanks -- I didn't have a good explanation of _why_ quoting here fixed this; that clears it up. – Charles Duffy Aug 14 '14 at 21:35
  • @chepner, thanks for the succinct explanation. Mine is at 4.1.2 – Dinesh Aug 19 '14 at 08:02
  • It's good that there is a simple workaround; 4.2 doesn't even seem to be widespread in distributions yet, so 4.3 as a default seems to be a ways out (even if distributions do leapfrog directly to 4.3). – chepner Aug 19 '14 at 12:33
1

There is no need to use read. IFS is all you need. (set -f or set -o noglob on as needed to prevent expansion):

#!/bin/bash
## if expansion of '*' or '?' is a concern, then uncomment the next line:
# set -f   # (or set -o noglob on)

x=abc:def:gh
IFS=$':'
arr=( $x )                          # simple assignment will split x


for ((i=0;i<${#arr[@]};i++)); do    # dump the loop values
    echo "arr[$i] ${arr[$i]}"
done

output:

arr[0] abc
arr[1] def
arr[2] gh

or with set -f and x='*':

arr[0] *
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • 1
    This method is subject to pathname expansion or requires disabling pathname expansion. Try `x='*'; arr=($x)`. – konsolebox Aug 15 '14 at 05:08
  • 1
    Grr.. - you're right of course, but given the problem description I did not see pathname expansion as a criteria. But presuming a need to protect against all possibilities, then you can add `set -f` or `set -o noglob on`. (thanks for keeping me on my toes :) – David C. Rankin Aug 15 '14 at 07:10