Create an array in bash from given start and end point [duplicate] - arrays

This question already has answers here:
How do I iterate over a range of numbers defined by variables in Bash?
(20 answers)
Closed 4 years ago.
I have this code that works:
$ array=( {100..300..100} )
$ for k in ${array[#]} ; do echo $k ; done
100
200
300
I want to parametrize the start and end points (and also the increment, because why not?)
I tried this:
$ low=100
$ high=300
$ incr=100
$ array=( {$low..$high..$incr} )
But it didn't work:
$ for k in ${array[#]} ; do echo $k ; done
{100..300..100}
What am I doing wrong?

From bash manual:
A sequence expression takes the form {x..y[..incr]}, where x and y are either integers or single characters, and incr, an optional increment, is an integer.
So, parameter and variable expansion are not performed on x, y and incr. You should use seq:
arr=( $(seq $low $incr $high) )

Related

how to use looping variables to populate an array [duplicate]

This question already has answers here:
How do I iterate over a range of numbers defined by variables in Bash?
(20 answers)
Closed last year.
when I populate an array using explicit range value it get populated correctly using the code below:
arr=()
for i in {0..10}
do
arr+=( 1 )
done
echo ${arr[#]}
and I got the following output (nothing surprising)
However, when storing the range in a variable and calling it as in the code below it only shows one element:
range=10
arr=()
for i in {0..$range}
do
arr+=( 1 )
done
echo ${arr[#]}
I got the following output
Variable expansion does not occur before brace expansion so you only get a literal {0..1} as a single iterated argument.
Use the old school method instead:
for (( i = 0; i <= range; ++i )); do
Used <= here because .. is inclusive.

How to give Array as argument in bash? [duplicate]

This question already has answers here:
Passing An Array From One Bash Script to Another
(3 answers)
How to pass an array argument to the Bash script
(8 answers)
Closed 2 years ago.
I use create.sh to create random passwords, where each pw is appened to an array which i echo out and of which i give the expanded version (ie. [#]) to the select script.
In the select script i cant access the array. I echoed its length, which said to be 1. even though 2 elements where in it when i printed seperated by a space.
Now i cant access the index of 1 because only index 0 exists which is all elements concatenated by a whitespace.
Thanks in advance.
Robert
create.sh
SEQ_LEN=1
MAX_CHAR=25
PASSWORDS=()
COUNTER=0
while getopts "x:l:" OPTION; do
case $OPTION in
x )
SEQ_LEN="$OPTARG"
;;
l )
MAX_CHAR="$OPTARG"
;;
\? )
echo "correct usage: create [-x] [SEQ_LEN] [-l] [MAX_CHAR]"
exit 1
;;
esac
done
shift "$(($OPTIND -1))"
for VAR in $(seq 1 $SEQ_LEN)
do
PASS=$(openssl rand -base64 48 | cut -c1-$MAX_CHAR)
PASSWORDS[$COUNTER]="$PASS"
COUNTER=$(($COUNTER +1))
done
echo "${PASSWORDS[#]}"
select.sh
#!/bin/bash
ARRAY=()
INDEX=0
while getopts "a:i:" OPTION; do
case $OPTION in
a )
ARRAY=$OPTARG
;;
i )
INDEX=$OPTARG
;;
\? )
echo "Correct Usage: select [-a] [PASSWORD_ARRAY] [-i] [SELECTED_INDEX]"
;;
esac
done
echo "$ARRAY $INDEX" # looks identical
echo "${#ARRAY[#]}" # is 1 - should be 2
Thanks for the ans wers and helpful advice.
Since bash is so great, it turns out you can just do
ARRAY=( $OPTARG )
and it splits the $OPTARG string by " ", turning it into an "array"
"Obviously" you can just for loop through the string and append to a new variable, that being declared as -a array. source
as seen below:
sentence="This is a sentence."
for word in $sentence
do
echo $word
done

What does # mean in front of an array name in bash script?

I read such a line:
arrayA=$((${#arrayB[#]}+${#arrayC[#]}));
What does it do? Especially what's the meaning of # in front of array name?
${#a[#]} expands to the number of elements in the array a. See the Shell Parameter Expansion section of the reference manual.
Thanks #gniourf_gniourf, but what is this line does in overall? I tried to run the rhs in my terminal with two arrays, and it seems to try to execute the sum result as a command? This doesn't make sense to me...
$((...)) is the arithmetic context.
${#arrayB[#]} and ${#arrayC[#]} expand to the number of elements in arrayB and arrayC respectively.
Hence $((${#arrayB[#]}+${#arrayC[#]})) expands to the sum of the number of elements in arrayB and arrayC. Check it with echo $((${#arrayB[#]}+${#arrayC[#]})).
Hence your snippet will assign to the variable arrayA the sum of the number of elements in arrayB and arrayC.
Demo:
$ arrayB=( one two three )
$ arrayC=( alpha beta gamma delta )
$ echo "${#arrayB[#]}"
3
$ echo "${#arrayC[#]}"
4
$ echo "$(( ${#arrayB[#]} + ${#arrayC[#]} ))"
7
$ arrayA=$(( ${#arrayB[#]} + ${#arrayC[#]} ))
$ echo "$arrayA"
7
The line:
arrayA=$((${#arrayB[#]}+${#arrayC[#]}));
Reads: set the value of variable arrayA as the summed length of arrayB and arrayC
$(()) is the arithmetic expansion syntax in which you have the sum + of the two array lengths ${#array[#]}

How to modify 2d array in shell script

I have the following sample code for my shell script:
#!/bin/bash
x[1,1]=0
x[2,1]=1
echo "x[1,1]=${x[1,1]}"
echo "x[2,1]=${x[2,1]}"
for i in {1..2}; do
x[$i,1]=${i}
echo "loop$i x[$i,1]=${i}"
done
echo "x[1,1]=${x[1,1]}"
echo "x[2,1]=${x[2,1]}"
and I am expecting for x[1,1] to have the value of 1 and x[2,2] to have the value of 2.
But when I run the script the result is:
$ ./test3.sh
x[1,1]=1
x[2,1]=1
loop1 x[1,1]=1
loop2 x[2,1]=2
x[1,1]=2
x[2,1]=2
I expect x[1,1] to retain the value of 1 but it happens to be 2 now. Is there something wrong with my script?
Bash does not have 2-D arrays. The best you can do is emulate them with associative arrays.
Add the following line to the beginning of your script:
declare -A x
This makes x into an associative array. When that is done, the script produces the output that you expect:
$ bash script
x[1,1]=0
x[2,1]=1
loop1 x[1,1]=1
loop2 x[2,1]=2
x[1,1]=1
x[2,1]=2
Bash indexed arrays
Unless declare -A is used, a bash array is just an indexed array. Let's define y as an indexed array:
$ y=()
Now, let's assign two values:
$ y[2,3]=1
$ y[22,3]=2
Now, let's use declare -p to find out what the contents of the array really are:
$ declare -p y
declare -a y='([3]="2")'
As you can see, there is only y[3]. The reason is that the index in an indexed array is subject to arithmetic expansion and, when given a list of comma-separated values, arithmetic expansion returns just the last one.
In other words, as far as bash is concerned, assignments to y[2,3] and y[22,3] are both just assignments to y[3]. The second assignment overwrites the first.
We can see this directly if we echo the results of arithmetic expansion:
$ echo $((3))
3
$ echo $((2,3))
3
$ echo $((22,3))
3
When given a list of comma-separated values, arithmetic expansion returns the last one. This is true even if the comma-separated list is a long one:
$ echo $((1+2,3*4,5,6,7,8))
8
It is always the last value which is returned.
Bash associative arrays
Let's examine what happens with associative arrays. Let's define z as an associative array and assign some values to it:
$ declare -A z
$ z[1,2]=1
$ z[3,4]=2
$ z["Jim Bob"]=3
Now, let's see what was stored in z:
$ declare -p z
declare -A z='([3,4]="2" ["Jim Bob"]="3" [1,2]="1" )'
This seems to be what you need.

how to iterate all elements in an array when some of the elements are not defined?

let's say:
$ a[0]=1
$ a[2]=2
then, when i do:
$ for b in "${a[#]}"; do echo $b; done
1
3
this is not what i want. since i skipped a[1], it's not extending in a good sense. the "undefined" indices are skipped. i hope it can show an empty line for those, i.e.:
1
3
and even the length of the array is not quite expected:
$ echo ${#a[#]}
2
making it explicit won't work too:
$ unset a
$ declare -a a
$ a[0]=1
$ a[2]=2
$ for b in "${a[#]}"; do echo $b; done
1
2
$ echo ${#a[#]}
2
so any solution to this? this leads to the inconsistency among array length, index, and iteration.
bash version is not too old:
$ bash --version
GNU bash, version 4.0.28(1)-release
(i know it's a shell shock vulnerable version. my poor company.)
What you describe is correct behavior for sparse arrays. However, if you want to iterate over a continuous range, because you know the start and end indices, you can simply do...
for (( i=0; i<=lastIndex; i++ )); do
echo "${array[i]}"
done
Where array[i] is unset, the expansion will be the empty string, so you'll get what you asked for.
If you don't know the indices, you can get them using another array:
indices=( "${!array[#]}" )
firstIndex="${indices[0]}"
lastIndex="${indices[-1]}"
for (( i=firstIndex; i<=lastIndex; i++ )); do
echo "${array[i]}"
done

Resources