I am pulling my hair out manipulating arrays in bash. I have an array of strings, which contain spaces. I would like an array containing all but the first element of my input array.
input=("first string" "second string" "third string")
echo ${#input[@]}
# len(input)=3
# get slice of all except for first element of input
slice=${input[@]:1}
echo ${#slice[@]}
# expect 2, but get 1
echo $slice
# second string third string
# slice should contain ("second string" "third string"), but instead is "second string third string"
Slicing the array clearly works to eliminate the first element, but the result appears to be a concatenation of all remaining strings, rather than an array. Is there a way to slice an array in bash and get an array as a result?
(sorry, I'm not new to bash, but I've never used it for much before, and I can't find any documentation showing why my slice is flattened)
CodePudding user response:
Indexes are reset, element 1 is now element 0:
slice=("${input[@]:1}")
Element and index are removed, the first element is now index 1, not index 0:
unset input[0]
${#slice[@]}
or ${#input[@]}
will now be 1 less than the previous value of ${#input[@]}
. Starting out with three elements in slice
, the values of "${!slice[@]}"
and "${!input[@]}"
, will be 0 1
and 1 2
respectively (for either the first or second approach)
If you don't quote slice=("${input[@]:1}")
, each array element is split on whitespace, creating many more elements.
CodePudding user response:
First off, you should always quote variable expansions. Be very wary of any solution that relies on unquoted expansions. ShellCheck.net is a great tool for catching bugs related to quoting (among many other issues).
To your specific issue, slice=${input[@]:1}
does not do what you want. It defines a single scalar variable slice
rather than an array, meaning the array expansion (denoted by the [@]
) will first be munged into a single string using the current IFS
. Here's a demo:
$ arr=(1 2 '3 4')
$ IFS=,
$ var="${arr[@]:1}"
$ echo "$var"
2,3 4
To instead declare and populate an array use the =()
notation, like so:
$ var=("${arr[@]:1}")
$ printf '%s\n' "${var[@]}"
2
3 4