1

When slicing an array the output is captured in a variable as a string.

When the slice is used directly it will be separate items.

How can I save a sliced array in a variable without it being turned into a string?

Any other method or way to go about this?

Example:

# setting IFS to this, no need to quote anything
IFS=$'\n'

mapfile -t arr < <(for i in {1..6}; do echo $i; done)

declare -p arr
declare -a arr=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6")

# $() or `` give the same results  
slc=`echo ${arr[@]:0:3}`  

# proving that when the slice is saved as a var, it's a string
for i in $slc; do echo $i; done
1 2 3

# proving that when the slice is used directly the items are separate
for i in ${arr[@]:0:3}; do echo $i; done
1
2
3

Got it working with the accepted answer, note; with or without quotes when IFS=$'\n' the results are the same:

IFS=$'\n'

slc=(${arr[@]:0:3})

for i in ${slc[@]}; do echo $i; done
1
2
3

1 Answer 1

7

In:

echo ${arr[@]:0:3}

First, you forgot the quotes which means the expansion is subject to split+glob (with IFS=$'\n', that's still splitting on newline, and that's still doing globbing), and used echo which cannot be used for arbitrary data.

echo is being passed several arguments (the elements of the slice further subjected to split+glob), and it prints them space-separated.

With:

var=`echo...`

(btw, that's the old deprecated form of command substitution, you should use var=$(echo...) instead)

the shell collects that output (minus the trailing newline characters) into a scalar variable as that's scalar variable assignment syntax, but anyway the information about where each element starts and ends has long been lost since echo bundled everything into one stream.

To store the slice into a variable, you need an array variable and use the array assignment syntax:

$ arr=( '*' $'\n' '' 'element '{4..6} ) # ¹
$ slc=( "${arr[@]:0:3}" )
$ typeset -p arr slc
declare -a arr=([0]="*" [1]=$'\n' [2]="" [3]="element 4" [4]="element 5" [5]="element 6")
declare -a slc=([0]="*" [1]=$'\n' [2]="")

Same as you'd do:

   @slc = @arr[0..2]         # in perl
    slc = $arr( `{seq 3} )   # in rc
set slc   $arr[1..3]         # in fish
    slc=( "${arr[1,3]}" )    # in yash
    slc=( "${(@)arr[1,3]}" ) # in zsh

And to loop over the elements:

for i in "${slc[@]}"; do
  something with "$i"
done

¹ or readarray -td '' arr < <(printf '%s\0' '*' $'\n' '' 'element '{4..6} if you have to use readarray or its misnomer mapfile alias, relying on the fact that bash variables or builtin arguments cannot contain NUL characters anyway.

3

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .