why it does not start from the first line of array? - arrays

code:
#!/bin/bash
aa=(1 2 3 4)
for i in "${aa[#]}";do
echo "${aa[$i]}"
done
exit 0
it will prints out:
2
3
4
i dont think theres an error in my code, I already double checked it with shellcheck too.

The loop iterates over the array's values, not its indices. It'll be clearer if you make the array values strings:
aa=(foo bar baz quux)
for entry in "${aa[#]}";do
echo "$entry"
done
Notice how I got rid of the aa[$i] lookup and just print the variable directly. This prints:
foo
bar
baz
quux
If you want to iterate over the indices add !:
for i in "${!aa[#]}";do
echo "${aa[$i]}"
done
Or use a C-style loop:
for ((i = 0; i < "${#aa[#]}"; i++)); do
echo "${aa[$i]}"
done

Related

Select a specific line of an array in a for loop

I have an array with multiple lines, and I would like to select a specific line of the array in a for loop. And then use the data in the selected line for some analysis in the for loop.
ARRAY=(A1,A2
B1,B2
C1,C2)
When I applied the below code, I can assign "B1" to Trait1 and "B2" to Trait2. This works for j=1 or j=3 as well.
j=2
echo ${ARRAY[$j-1]}
Trait1="$(echo ${ARRAY[$j-1]} | cut -d',' -f1)"
Trait2="$(echo ${ARRAY[$j-1]} | cut -d',' -f2)"
echo $Trait1
echo $Trait2
Ultimately, I want to put the above code in a for loop. But this failed.
nLine=3
for j in $(eval echo "{1..$nLine}")
do
Trait1="$(echo ${ARRAY[$j-1]} | cut -d',' -f1)"
Trait2="$(echo ${ARRAY[$j-1]} | cut -d',' -f2)"
echo $Trait1
echo $Trait2
done
-bash: 1 2 3-1: syntax error in expression (error token is "2 3-1")
-bash: 1 2 3-1: syntax error in expression (error token is "2 3-1")
Thank you.
Keeping the current code design I'd probably want to use parameter expansion to break the array element into its 2 components ...
Data setup:
$ ARRAY=(A1,A2
B1,B2
C1,C2)
$ typeset -p ARRAY
declare -a ARRAY=([0]="A1,A2" [1]="B1,B2" [2]="C1,C2")
Quick demo of parameter expansion to break array element into parts:
$ x=${ARRAY[0]}
$ echo ${x}
A1,A2
$ echo ${x%,*}
A1
$ echo ${x#*,}
A2
Updating the PO's for loop to use parameter expansion, and replacing the eval/echo/j-1 with something a bit easier to read:
nLine=3
for (( j=0 ; j<nLine ; j++ ))
do
Trait1="${ARRAY[${j}]%,*}"
Trait2="${ARRAY[${j}]#*,}"
echo "${Trait1}"
echo "${Trait2}"
done
Generates the output:
A1
A2
B1
B2
C1
C2
The way you're using it, you should have a single string, not an array.
ARRAY='A1,A2
B1,B2
C1,C2'
Then when you use it as a here-string input to sed, it will receive multiple lines of input, and you can select the line you want with ${j}p.
As mentioned in the comments, this seems like a convoluted way to access array elements. You can simply use the shell's built-in array indexing operator.
Array assignments
ARRAY=(A1,A2
B1,B2
C1,C2)
and
ARRAY=(A1,A2 B1,B2 C1,C2)
are equivalent and both define an array with 3 elements: A1,A2, B1,B2, and C1,C2. Space(s), tab(s) and new-line(s) are used to separate array elements in compound assignments.
A for loop can be used to traverse an array:
# "${ARRAY[#]}" expands each element of ARRAY to a separate word
for elm in "${ARRAY[#]}"; do
# The value of the array element accessed is "$elm"
done
Or, using the alternate form of the for loop:
# ${#ARRAY[*]} expands to the number of elements in the ARRAY
for ((j = 0; j < ${#ARRAY[*]}; ++j)); do
# Array element can be referenced using ${ARRAY[j]}
done
assuming there is no gap between indices (bash allows an array with "holes").
Here strings, a variant of here documents, and read builtin with IFS variable can be used to parse the elements of the array:
IFS=, read Trait1 Trait2 <<< ${ARRAY[j]}
Here the IFS is used with the value , to cut the array element into Trait1 and Trait2 when there is a ,.
So, a sample program to demonstrate all of these may be:
#!/bin/bash
ARRAY=(A1,A2
B1,B2
C1,C2)
for elem in "${ARRAY[#]}"; do
IFS=, read Trait1 Trait2 <<< $elem
printf "%s\n%s\n" "$Trait1" "$Trait2"
done
or,
#!/bin/bash
ARRAY=(A1,A2
B1,B2
C1,C2)
for ((j = 0; j < ${#ARRAY[*]}; ++j)); do
IFS=, read Trait1 Trait2 <<< ${ARRAY[j]}
printf "%s\n%s\n" "$Trait1" "$Trait2"
done

Append elements to an array in bash

I tried the += operator to append an array in bash but do not know why it did not work
#!/bin/bash
i=0
args=()
while [ $i -lt 5 ]; do
args+=("${i}")
echo "${args}"
let i=i+1
done
expected results
0
0 1
0 1 2
0 1 2 3
0 1 2 3 4
actual results
0
0
0
0
0
It did work, but you're only echoing the first element of the array. Use this instead:
echo "${args[#]}"
Bash's syntax for arrays is confusing. Use ${args[#]} to get all the elements of the array. Using ${args} is equivalent to ${args[0]}, which gets the first element (at index 0).
See ShellCheck: Expanding an array without an index only gives the first element.
Also btw you can simplify let i=i+1 to ((i++)), but it's even simpler to use a C-style for loop. And also you don't need to define args before adding to it.
So:
#!/bin/bash
for ((i=0; i<5; ++i)); do
args+=($i)
echo "${args[#]}"
done

For loop for an array in bash

If I want to print out the contents from the 13 element till the second last element of an array and I don't know how long the array is, is this how it would be done with BASH?
for array_element in `seq 13 $(({myarray[#]-1))`
do
echo ${myarray[array_element]}
done
Since you're using bash, don't use seq. Instead, use a C-style for loop.
for ((i=13; i < ${#myarray[#]} - 1; i++)); do
echo ${myarray[i]}
done
You could do it like this:
for array_element in `seq $((${#myarray[#]}-1))`
do
echo ${myarray[$((array_element-1))]}
done

change array value while looping in bash

The code
SourceFolder[0]=""
SourceFolder[1]="inbound2"
SourceFolder[2]="inbound3"
for i in "${!SourceFolder[#]}"
do
if [ -z "${SourceFolder[$i]}"]; then
${SourceFolder[$i]} = "TEST"
fi
done
${SourceFolder[$i]} = "TEST" - doesn't work
it says
=: command not found
How to change value in current loop index in an array?
Because of the first space = is not interpreted as an assignment. There is a full explanation on So.
Btw ${SourceFolder[$i]} evaluate the array element, which is not what you want to do. For instance for the first one it is the empty string.
Replaces with SourceFolder[$i]=
You must change indexnumber in the your array:
ARRAYNAME[indexnumber]=value
ok, you have array is:
array=(one two three)
you can add count to your script for initialize and change element for array indexnumber, example:
#!/bin/bash
count=0
array=(one two three)
for i in ${array[#]}
do
echo "$i"
array[$count]="$i-indexnumber-is-$count"
count=$((count + 1))
echo $count
done
echo ${array[*]}
Result:
bash test-arr.sh
one
1
two
2
three
3
one-indexnumber-is-0 two-indexnumber-is-1 three-indexnumber-is-2

Returning array from a Bash function

I am making a bash script and I have encountered a problem. So let's say I got this
function create_some_array(){
for i in 0 1 2 3 .. 10
do
a[i]=$i
done
}
create_some_array
echo ${a[*]}
Is there any way I can make this work? I have searched quite a lot and nothing I found worked.
I think making the a[] a global variable should work but I can't find something that actually works in my code. Is there any way to return the array from the function to main program?
Thanks in advance
This won't work as expected when there are whitespaces in the arrays:
function create_some_array() {
local -a a=()
for i in $(seq $1 $2); do
a[i]="$i $[$i*$i]"
done
echo ${a[#]}
}
and worse: if you try to get array indices from the outside "a", it turns out to be a scalar:
echo ${!a[#]}
even assignment as an array wont help, as possible quoting is naturally removed by the echo line and evaluation order cannot be manipulated to escape quoting: try
function create_some_array() {
...
echo "${a[#]}"
}
a=($(create_some_array 0 10))
echo ${!a[#]}
Still, printf seems not to help either:
function create_some_array() {
...
printf " \"%s\"" "${a[#]}"
}
seems to produce correct output on one hand:
$ create_some_array 0 3; echo
"0 0" "1 1" "2 4" "3 9"
but assignment doesn't work on the other:
$ b=($(create_some_array 0 3))
$ echo ${!b[#]}
0 1 2 3 4 5 6 7
So my last trick was to do assignment as follows:
$ eval b=("$(create_some_array 0 3)")
$ echo -e "${!b[#]}\n${b[3]}"
0 1 2 3
3 9
Tataaa!
P.S.: printf "%q " "${a[#]}" also works fine...
This works fine as described. The most likely reason it doesn't work in your actual code is because you happen to run it in a subshell:
cat textfile | create_some_array
echo ${a[*]}
would not work, because each element in a pipeline runs in a subshell, and
myvalue=$(create_some_array)
echo ${a[*]}
would not work, since command expansion happens in a subshell.
You can make an array local to a function, and then return it:
function create_some_array(){
local -a a=()
for i in $(seq $1 $2); do
a[i]=$i
done
echo ${a[#]}
}
declare -a a=()
a=$(create_some_array 0 10)
for i in ${a[#]}; do
echo "i = " $i
done
Hi here is my solution:
show(){
local array=()
array+=("hi")
array+=("everything")
array+=("well?")
echo "${array[#]}"
}
for e in $(show);do
echo $e
done
Try this code on: https://www.tutorialspoint.com/execute_bash_online.php
Both these work for me with sh and bash:
arr1=("192.168.3.4" "192.168.3.4" "192.168.3.3")
strArr=$(removeDupes arr1) # strArr is a string
for item in $strArr; do arr2+=("$item"); done # convert it to an array
len2=${#arr2[#]} # get array length
echo "${len2}" # echo length
eval arr3=("$(removeDupes arr1)") # shellcheck does not like this line and won't suppress it but it works
len3=${#arr3[#]} # get array length
echo "${len3}" # echo length
As an aside, the removeDupes function looks like this:
removeDupes() {
arg="$1[#]"
arr=("${!arg}")
len=${#arr[#]}
resultArr=()
# some array manipulation here
echo "${resultArr[#]}"
}
This answer is based on but better explains and simplifies the answers from #Hans and #didierc

Resources