How to find the length of an array in shell? - arrays

How do I find the length of an array in shell?
For example:
arr=(1 2 3 4 5)
And I want to get its length, which is 5 in this case.

$ a=(1 2 3 4)
$ echo ${#a[#]}
4

From Bash manual:
${#parameter}
The length in characters of the expanded value of parameter is substituted. If parameter is ‘’ or ‘#’, the value substituted is the
number of positional parameters. If parameter is an array name
subscripted by ‘’ or ‘#’, the value substituted is the number of
elements in the array. If parameter is an indexed array name
subscripted by a negative number, that number is interpreted as
relative to one greater than the maximum index of parameter, so
negative indices count back from the end of the array, and an index of
-1 references the last element.
Length of strings, arrays, and associative arrays
string="0123456789" # create a string of 10 characters
array=(0 1 2 3 4 5 6 7 8 9) # create an indexed array of 10 elements
declare -A hash
hash=([one]=1 [two]=2 [three]=3) # create an associative array of 3 elements
echo "string length is: ${#string}" # length of string
echo "array length is: ${#array[#]}" # length of array using # as the index
echo "array length is: ${#array[*]}" # length of array using * as the index
echo "hash length is: ${#hash[#]}" # length of array using # as the index
echo "hash length is: ${#hash[*]}" # length of array using * as the index
output:
string length is: 10
array length is: 10
array length is: 10
hash length is: 3
hash length is: 3
Dealing with $#, the argument array:
set arg1 arg2 "arg 3"
args_copy=("$#")
echo "number of args is: $#"
echo "number of args is: ${##}"
echo "args_copy length is: ${#args_copy[#]}"
output:
number of args is: 3
number of args is: 3
args_copy length is: 3

Assuming bash:
~> declare -a foo
~> foo[0]="foo"
~> foo[1]="bar"
~> foo[2]="baz"
~> echo ${#foo[*]}
3
So, ${#ARRAY[*]} expands to the length of the array ARRAY.

in tcsh or csh:
~> set a = ( 1 2 3 4 5 )
~> echo $#a
5

In the Fish Shell the length of an array can be found with:
$ set a 1 2 3 4
$ count $a
4

This works well for me:
arglen=$#
argparam=$*
if [ $arglen -eq '3' ];
then
echo Valid Number of arguments
echo "Arguments are $*"
else
echo only four arguments are allowed
fi

For those who still searching a way to put the length of an array into a variable:
foo="${#ARRAY[*]}"

Related

Bash: Count total number of keys in an associative array?

Consider the associative array below:
declare -A shapingTimes
shapingTimes=([0-start]=15 [0-stop]=21 [0-anotherkey]=foo)
shapingTimes+=([1-start]=4 [1-stop]=6 [1-anotherkey]=bar)
shapingTimes+=([2-start]=9 [2-stop]=11 [2-anotherkey]=blah)
Is there a way to find the total number of keys used per entry in an array? (Is this per 'index' in an array?)
For example, how to count: [start], [stop], [anotherkey] as = 3 keys?
At the moment I'm using the hardcoded value (3) from this code I found (as below) that does the job fine, but I'm wondering if this can be achieved dynamically?
totalshapingTimes=$((${#shapingTimes[*]} / 3))
I've found these variables that return various array aspects, but not the total number of keys.
echo "All of the items in the array:" ${shapingTimes[*]}
echo "All of the indexes in the array:" ${!shapingTimes[*]}
echo "Total number of items in the array:" ${#shapingTimes[*]}
echo "Total number of KEYS in each array entry:" #???
Desired output:
All of the items in the array: 21 6 11 blah 15 4 bar 9 foo
All of the indexes in the array: 0-stop 1-stop 2-stop 2-anotherkey 0-start 1-start 1-anotherkey 2-start 0-anotherkey
Total number of items in the array: 9
Total number of KEYS in each array entry: 3
declare -A shapingTimes
shapingTimes=([0-start]=15 [0-stop]=21 [0-anotherkey]=foo)
shapingTimes+=([1-start]=4 [1-stop]=6 [1-anotherkey]=bar)
shapingTimes+=([2-start]=9 [2-stop]=11 [2-anotherkey]=blah)
# output all keys
for i in "${!shapingTimes[#]}"; do echo $i; done
Output:
1-start
2-stop
1-stop
0-start
2-start
2-anotherkey
1-anotherkey
0-stop
0-anotherkey
# Leading numbers and "-" removed:
for i in "${!shapingTimes[#]}"; do echo ${i/#[0-9]*-/}; done
Output:
start
stop
stop
start
start
anotherkey
anotherkey
stop
anotherkey
# put shortend keys in new associative array
declare -A hash
for i in "${!shapingTimes[#]}"; do hash[${i/#[0-9]*-/}]=""; done
echo "${#hash[#]}"
Output:
3
Once you key your array names with -, there isn't a direct way to identify the count of occurrences of the string past the - character.
One way would be to use additional tools to identify the count. The "${!shapingTimes[#]}" prints all the keys of the array and sort -ut- k2 goes a unique sort based on the 2nd field following the - delimiter, which is piped to wc -l to get the line count.
printf '%s\n' "${!shapingTimes[#]}" | sort -ut- -k2 | wc -l
3
Same as #Cyrus's solution, but with no loop and no sub-shell:
#!/usr/bin/env bash
declare -A shapingTimes=(
[0-start]=15 [0-stop]=21 [0-anotherkey]=foo
[1-start]=4 [1-stop]=6 [1-anotherkey]=bar
[2-start]=9 [2-stop]=11 [2-anotherkey]=blah
)
# Populate simple array with keys only
declare -a shapingTimesKeys=("${!shapingTimes[#]}")
# Create associative array entries declaration string
# populated by prefix stripped keys
printf -v _str '[%q]= ' "${shapingTimesKeys[#]/#[0-9]*-/}"
# Declare the stripped keys associative array using
# the string populated just above
declare -A shapingTimesHash="($_str)"
printf 'Found %d keys:\n' "${#shapingTimesHash[#]}"
printf '%q\n' "${!shapingTimesHash[#]}"

Error in getting array length in bash after passing to function

What is wrong in this approach I can't get correct value of array length
#!/bin/bash
foo(){
val=$#
len=${#val[#]}
echo "Array contains: " $val
echo "Array length is: " $len
}
var=(1 2 3)
foo ${var[#]}
Output:
Array contains: 1 2 3
Array length is: 1
Change val=$# to val=("${#}") and you should be fine.
This answer in unix.stackexchange explains why:
You're flattening the input into a single value.
You should do
list=("${#}")
to maintain the array and the potential of whitespace in
arguments.
If you miss out the " then something like ./script.sh "a b" 2 3 4 will
return a length of 5 because the first argument will be split up

Array in Bash: Displaying all elements of array

echo "Enter N " # enter N for number of inputs for the loop
read N # reading the N
#using c-style loop
for((i=1;i<=N;i++))
do
read -a arr # arr is the name of the array
done
echo ${arr[*]} # 1
echo ${arr[#]} # 2
Tried all the ways to display all the elements of the array but not getting the desired output. It's displaying the last element of the array.
To be able to populate an array in loop use:
arr+=("$var")
Full code:
read -p 'Enter N: ' N
arr=() # initialize an array
# loop N times and append into array
for((i=1;i<=N;i++)); do
read a && arr+=("$a")
done
you are reading the data in an array arr and trying to print array
You keep redefining array with read -a. The code should be written like this instead:
#!/bin/bash
echo "Enter N " # enter N for number of inputs for the loop
read N # reading the N
#using c-style loop
declare -a array
for((i=1;i<=N;i++))
do
read array[$i] # arr is the name of the array
done
echo ${array[*]} # 1
echo ${array[#]} # 2
There are probably better ways to doing this. I just wanted to show how to fix your current code.
Example run
$ bash ./dummy.sh
Enter N
2
3
4
3 4
3 4
Hopefully this help other's who have the same issues.
Display all contents of the array in the shell:
"${arr[*]}"
Cleaning up your script (not sure what your intention was, though):
read -p "Enter N " N # User inputs the number of entries for the array
ARR=() # Define empty array
#using c-style loop
for ((i=1;i<=N;i++))
do
read -p "Enter array element number $N: " ADD # Prompt user to add element
ARR+=($ADD) # Actually add the new element to the array.
done
echo "${ARR[*]}" # Display all array contents in a line.
I found a similar solution from #choroba at: How to echo all values from array in bash

bash function returns with changed indexes

I have following code:
#!/bin/bash
function getSlots {
declare -A slots
for index in `seq 7 10`;
do
slots[$index]=$index
done
echo ${slots[#]}
}
slots=($(getSlots))
for i in ${!slots[#]};
do
echo "$i ${slots[$i]}"
done
When I run, my output is this:
0 10
1 7
2 8
3 9
Why do indexes change when I call the function?
arr=(...) re-indexes the array.
With your current approach, you cannot preserve the indexes as any information about them is lost as soon as you leave the function (you just echo the values).
You can use nameref (requires bash 4.3 or later) to modify the supplied array directly and since you are using only numbers as indexes, regular array will suffice:
#!/usr/bin/env bash
function get_slots {
local index # makes index local
local -n _arr=$1 # _arr points to the supplied array
_arr=() # empties the array
for index in {7..10}; do
_arr[index]=$index # assigns new values to the array
done
}
get_slots slots
for i in "${!slots[#]}"; do
echo "$i ${slots[$i]}"
done
Because the echo ${slots[#]} at the end of the function getSlots expands to echo 10 7 8 9 and that output is what you are assigning to the array slots by doing:
slots=($(getSlots))
Another interesting question would be why echo ${slots[#]} expands to echo 10 7 8 9 and not to echo 7 8 9 10.
That is because the slots inside getSlots is declared as an associative array, not an array, i.e.:
declare -A slots
Replace the line above with:
declare -a slots
and you will get the following output:
0 7
1 8
2 9
3 10

Bash Array - Cant get input from a loop in to my array correctly

I run this script as
script.sh 'abcdefghijk123456789!##$%^&' 'aaaa4444ged###'
it should be able to produce an array containing something like
1 1 1 1 15 15 15 15 7 5 23 23 23
with 12 indexes 0 - 11, but what i get is
1 1 1 1 1 5 1 5 1 5 1 5 7 5 2 3 2 3 2 3
with 20 indexes.
I want to populate a array with base numbers for a given char.
So i have a list of chars that we will be using in $startstring ie.
abcc5678% we can say that every char in $startstring is = to one char in the
$charset ie. abcd5678!%. This code finds what each $startstring char is equal
to $charset's index number. That information is what i am trying to capture in a
array. Mostly it works except a bug where instead of the whole number 10 getting
stored in decoded[1] what happens is the number 10 is split in to 1 and 0 and
then both are put in under separate indexes. So instead of a "1 10 1" and 3
indexes i end up with 4 indexes with "1 1 0 1". Im sure im just handling my
variables the wrong way but i searched and searched and now my brain is gonna
explode so i came here for some relief. or hope of it anyway. Can someone tell
me the proper way to insert digits in to this decoded[] array?
#!/bin/bash
#declare -i decoded
charset=$1
startstring=$2
start=$((${#charset}-1))
echo "Setting up CharMap to CharSet"
for i in $(eval echo {0..$start})
do
echo $i " = " ${charset:$i:1}
done
echo "Proving Chars Were Mapped Correctly."
start2=$((${#startstring}))
start3=$((${#charset}-1))
for i in $(eval echo {0..$start2})
do
for p in $(eval echo {0..$start3})
do
if [ "${startstring:$i:1}" == "${charset:$p:1}" ]
then
echo "found that" ${startstring:$i:1}"=" $p 'from the charmap.'
decoded+=$p #<--### I DONT THINK THIS IS WHAT I NEED ###
fi
done
done
##################Just trying to print my new array#########################
start4=$((${#decoded}-1))
echo 'Testing the array $decoded'
echo 'the var start4(length of $decoded) = '$start4
echo 'this number should equal ----------> '$start2
echo 'Printing out the $decoded array in a for loop'
for c in $(eval echo {0..$start4})
do
echo ${decoded[$c]} ###DOESNT WORK LIKE I THOUGHT# also tried echo ${decode:$c:1}
done
decoded+=$p appends $p as a string, not as an array entry. Essentially, you're creating the string "11111515151575232323" by appending all the index numbers together. (Actually, I get "00001414141464322222227" from your example, because of index bound problems. I'll let you worry about that...)
To store the decoded values as an array, set decoded to an empty array before the loop, and then use decoded+=("$p") to add $p as an element:
decoded=() # create an empty array
for i in $(eval echo {0..$start2})
do
for p in $(eval echo {0..$start3})
do
if [ "${startstring:$i:1}" == "${charset:$p:1}" ]
then
echo ${startstring:$i:1} "=" $p
decoded+=("$p") # append $p as a new array element
fi
done
done
Then, to get the size of the array (rather than a string length), use ${#decoded[#]}:
start4=$((${#decoded[#]}-1))

Resources