5

Is there a more compact alternative to

arr=( 1 2 3 )
e1=${arr[0]}
e2=${arr[1]}
e3=${arr[2]}

?

Something like,

e1, e2, e3=${arr[@]}
6
  • How are you assigning to the array in the first place? It's likely to make more sense to fix at that point. Commented Jul 21, 2017 at 15:47
  • @CharlesDuffy Good question! My script receives a string like 12,343,564. I then convert it to an array of 3 elements: arr=(${input//,/ }). Then I wannna unpack arr. Commented Jul 21, 2017 at 15:49
  • 5
    Don't do arr=(${...}) in the first place. IFS=, read -r one two three _ <<<"$input" is cleaner, more direct, less buggy (for instance, if you had input='1,2,*', you'd get an arr with a list of filenames in it due to glob expansion happening at the same point where string-splitting does). BTW, in the above, the _ soaks up any items after the third -- so if you had 12,343,565,foobar,baz, then foobar,baz would be assigned to _ rather than appended to the same element with 565. Commented Jul 21, 2017 at 15:49
  • 3
    IFS=, read changes IFS only for the duration of the one read command. It doesn't impact any other part of your script. Commented Jul 21, 2017 at 15:53
  • 2
    And if your script is relying on string-splitting (the primary behavior controlled by IFS), it almost certainly has other bugs as well. Consider reading through BashPitfalls -- and take the warning at the very top to heart. (BTW, pitfall #50 is directly on-point). Commented Jul 21, 2017 at 15:54

2 Answers 2

2

Generally speaking, asking this question implies that you shouldn't be using an array for your data in the first place.

That said, the following function is reusable and correct -- with no caveats around which data it can and can't be used for:

array_to_vars() {
  declare -n _arr=$1
  local var
  for var; do
    shift || return
    printf -v "$var" %s "$1"
  done
}

...usable as:

# put first element of arr into e1, second into e2, third into e3
# disregard the rest
array_to_vars arr e1 e2 e3

It's not as short as one might like, but it's less likely to cause bugs as something that works only for data not containing a sigil.


Let's say you're populating it like so:

read -r -a arr < <(command-that-generates-a-list)

You could replace that with:

read -r e1 e2 e3

Or let's say it's:

arr=( )
while read -r line; do
  arr+=( "$line" )
done < <(command-that-generates-a-list)

You could that replace that with:

{ read -r e1; read -r e2; read -r e3; } < <(command-that-generates-a-list)

or with:

{ IFS=$'\n' read -r -d '' e1 e2 e3; } < <(command-that-generates-a-list && printf '\0')

Or let's say it's:

arr=(${string//,/ })

...in that case, it would be simpler and more correct (avoiding undesired behaviors like glob expansion -- see BashPitfalls #50) to use:

IFS=, read -r e1 e2 e3 <<<"$string"
Sign up to request clarification or add additional context in comments.

1 Comment

The proposed solution isn't working. One point I don't understand is that the array _arr declared in the function array_to_vars isn't used at all.
1

If you don't have whitespaces in your array element then you can use read using default IFS value (whitespace) in shell:

arr=( 1 2 3 )

# unset IFS to default value it has been set earlier
unset IFS

# read content in 3 variables
read e1 e2 e3 <<< "${arr[*]}"

# examine the content of variables
declare -p e1 e2 e3
declare -- e1="1"
declare -- e2="2"
declare -- e3="3"

4 Comments

Neat. Much better than what I'm doing!
<<< "${arr[@]}" is well into the area of unspecified behavior. I'd at least suggest <<< "${arr[*]}" to be explicit to readers about how this works (putting the first character of $IFS between each element), and to reduce the syntactic ambiguity. (I'd also suggest read -r to avoid mangling backslashes, and perhaps specifying a single-character value for IFS to reduce the number of potential datums that will cause values to be read incorrectly).
"If you don't have whitespace" is a big assumption for an array, which is typically used because its elements contain whitespace.
From OP's question it appears only numbers are being stored in array.

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.