I need explenation about the following behavior of arrays in shell scripting:
Imagine the following is given:
arber@host ~> ls
fileA fileB script.sh
Now i can do the following commands:
arber@host ~> ARR=($(ls -d file*))
arber@host ~> echo ${ARR[0]} # start index 0
arber@host ~> echo ${ARR[1]} # start index 1
fileA
arber@host ~> echo ${ARR[2]} # start index 2
fileB
But when I do this via script.sh it behaves different (Start Index = 0):
arber@host ~> cat script.sh
#!/bin/bash
ARR=($(ls -d file*))
# get length of an array
aLen=${#ARR[@]}
# use for loop read all items (START INDEX 0)
for (( i=0; i<${aLen}; i++ ));
do
echo ${ARR[$i]}
done
Here the result:
arber@host ~> ./script.sh
fileA
fileB
I use Ubuntu 18.04 LTS and zsh. Can someone explain this?
bash
array indexing starts at 0
(always)zsh
array indexing starts at 1
(unless option KSH_ARRAYS
is set)To always get consistent behaviour, use:
${array[@]:offset:length}
For code which works in both bash
and zsh
, you need to use the offset:length
syntax rather than the [subscript]
syntax.
Even for zsh
-only code, you'll still need to do this (or use emulate -LR zsh
) since zsh
's array subscripting basis is determined by the KSH_ARRAYS
option.
Eg, to reference the first element in an array:
${array[@]:0:1}
Here, array[@]
is all the elements, 0
is the offset (which always is 0-based), and 1
is the number of elements desired.
Arrays in Bash are indexed from zero, and in zsh they're indexed from one.
But you don't need the indices for a simple use case such as this. Looping over ${array[@]}
works in both:
files=(file*)
for f in "${files[@]}"; do
echo "$f"
done
In zsh you could also use $files
instead of "${files[@]}"
, but that doesn't work in Bash. (And there's the slight difference that it drops empty array elements, but you won't get any from file names.)
Also, don't use $(ls file*)
, it will break if you have filenames with spaces (see WordSpliting on BashGuide), and is completely useless to begin with.
The shell is perfectly capable of generating filenames by itself. That's actually what will happen there, the shell finds all files with names matching file*
, passes them to ls
, and ls
just prints them out again for the shell to read and process.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With