BASH: loop array values unlimited - bash

In bash I want array let say:
array=(1 2 3)
Then I need a loop for program where
x will be 1,2,3,1,2,3... (from array)
i will be unlimited 1,2,3,4,5,6.... (main loop)
My code:
array=(1 2 3)
while true ; do
((i=i+1))
#screen -dmS plot$i -d /destinatin$x
echo $i $x
sleep 1
done
I do not know how to loop array and set $x to go 1,2,3,1,2,3....

Infinite loops are generally generated using the shell built-in command : which does nothing in its singular form. So if you want to loop infinitely over the elements of a list, you can do the following:
1. The infinite nested while-for loop:
while :; do for i in "${a[#]}"; do echo "${i}"; done; done
2. using an index-reset
i=0; while :; do echo "${a[i]}"; ((i=i+1)); ((i==${#a[#]})) && i=0; done
2. using modulo calculation:
i=0; while :; do echo "${a[i]}"; (( i=(i+1) % ${#a[#]} )); done
3. the infinite for loop with modulo index
for ((i=0;;i++)); do echo "${a[i%${#a[#]}]}"; done

This code should solve your problem:
#!/bin/bash
array=(1 2 3)
i=0
count_of_elements=${#array[#]} #counting the number of array elements
while true; do
rest=$(($i%$count_of_elements)) #counting rest of the division by count of array elements
printf "${array[$rest]}," #dispay result
i=$((i+1))
done
It will be also working if you change your input array (for example if it will be array=(1 2 3 4 5).

Related

Dynamic beginning of for loop

Create bash for loop with two alternative starts. I would like to either iterate through all numbers in a range or all elements in an array depending on a condition. The following unorthodox code shows the example:
#!/bin/bash
FROMVAL=1
TOVAL=5
VALARR=(1 3 5)
ISCONSEQ=1
if (( ISCONSEQ == 1 )); then
for (( counter=$FROMVAL; counter<=$TOVAL; counter++ ))
else
for counter in "${VALARR[#]}"
fi
do
echo $counter
done
Obviously, this does not work nor is it pretty. Is there a bash way to do this or should I create two for loops and execute only one depending on the content of ISCONSEQ? Or should I use a for...in loop for both cases and simply assign the values of my sequence to the array?
This code does work but relies only on the array:
if (( ISCONSEQ == 1 )); then
VALARR=($(seq $FROMVAL 1 $TOVAL))
fi
for counter in "${VALARR[#]}"
do
echo $counter
done
This is a good use case for a function. Create function for the echo and call it from each for loop. It would look like this.
#!/bin/bash
FROMVAL=1
TOVAL=5
VALARR=(1 3 5)
ISCONSEQ=1
function dostuff() {
echo $*
}
if (( ISCONSEQ == 1 )); then
for (( counter=$FROMVAL; counter<=$TOVAL; counter++ ))
do
dostuff $counter
done
else
for counter in "${VALARR[#]}"
do
dostuff $counter
done
fi
First, I agree with the previous comments that 2 for loops is not as offensive as it may look.
Second, you can't conditionally kick off a for loop in the manner of your example. But, you can manipulate your array with a conditional.
So to satisfy your question, here would be a simple solution:
FROMVAL=1
TOVAL=5
VALARR=(1 3 5)
ISCONSEQ=0
if (( ISCONSEQ != 1))
then
VALARR=($(seq $FROMVAL 1 $TOVAL))
fi
for counter in "${VALARR[#]}"
do
echo "$counter"
done

The meaning of ${TF_CFLAGS[#]} [duplicate]

How do I create an array in unix shell scripting?
The following code creates and prints an array of strings in shell:
#!/bin/bash
array=("A" "B" "ElementC" "ElementE")
for element in "${array[#]}"
do
echo "$element"
done
echo
echo "Number of elements: ${#array[#]}"
echo
echo "${array[#]}"
Result:
A
B
ElementC
ElementE
Number of elements: 4
A B ElementC ElementE
in bash, you create array like this
arr=(one two three)
to call the elements
$ echo "${arr[0]}"
one
$ echo "${arr[2]}"
three
to ask for user input, you can use read
read -p "Enter your choice: " choice
Bourne shell doesn't support arrays. However, there are two ways to handle the issue.
Use positional shell parameters $1, $2, etc.:
$ set one two three
$ echo $*
one two three
$ echo $#
3
$ echo $2
two
Use variable evaluations:
$ n=1 ; eval a$n="one"
$ n=2 ; eval a$n="two"
$ n=3 ; eval a$n="three"
$ n=2
$ eval echo \$a$n
two
#!/bin/bash
# define a array, space to separate every item
foo=(foo1 foo2)
# access
echo "${foo[1]}"
# add or changes
foo[0]=bar
foo[2]=cat
foo[1000]=also_OK
You can read the ABS "Advanced Bash-Scripting Guide"
The Bourne shell and C shell don't have arrays, IIRC.
In addition to what others have said, in Bash you can get the number of elements in an array as follows:
elements=${#arrayname[#]}
and do slice-style operations:
arrayname=(apple banana cherry)
echo ${arrayname[#]:1} # yields "banana cherry"
echo ${arrayname[#]: -1} # yields "cherry"
echo ${arrayname[${#arrayname[#]}-1]} # yields "cherry"
echo ${arrayname[#]:0:2} # yields "apple banana"
echo ${arrayname[#]:1:1} # yields "banana"
Try this :
echo "Find the Largest Number and Smallest Number of a given number"
echo "---------------------------------------------------------------------------------"
echo "Enter the number"
read n
i=0
while [ $n -gt 0 ] #For Seperating digits and Stored into array "x"
do
x[$i]=`expr $n % 10`
n=`expr $n / 10`
i=`expr $i + 1`
done
echo "Array values ${x[#]}" # For displaying array elements
len=${#x[*]} # it returns the array length
for (( i=0; i<len; i++ )) # For Sorting array elements using Bubble sort
do
for (( j=i+1; j<len; j++ ))
do
if [ `echo "${x[$i]} > ${x[$j]}"|bc` ]
then
t=${x[$i]}
t=${x[$i]}
x[$i]=${x[$j]}
x[$j]=$t
fi
done
done
echo "Array values ${x[*]}" # Displaying of Sorted Array
for (( i=len-1; i>=0; i-- )) # Form largest number
do
a=`echo $a \* 10 + ${x[$i]}|bc`
done
echo "Largest Number is : $a"
l=$a #Largest number
s=0
while [ $a -gt 0 ] # Reversing of number, We get Smallest number
do
r=`expr $a % 10`
s=`echo "$s * 10 + $r"|bc`
a=`expr $a / 10`
done
echo "Smallest Number is : $s" #Smallest Number
echo "Difference between Largest number and Smallest number"
echo "=========================================="
Diff=`expr $l - $s`
echo "Result is : $Diff"
echo "If you try it, We can get it"
Your question asks about "unix shell scripting", but is tagged bash. Those are two different answers.
The POSIX specification for shells does not have anything to say about arrays, as the original Bourne shell did not support them. Even today, on FreeBSD, Ubuntu Linux, and many other systems, /bin/sh does not have array support. So if you want your script to work in different Bourne-compatible shells, you shouldn't use them. Alternatively, if you are assuming a specific shell, then be sure to put its full name in the shebang line, e.g. #!/usr/bin/env bash.
If you are using bash or zsh, or a modern version of ksh, you can create an array like this:
myArray=(first "second element" 3rd)
and access elements like this
$ echo "${myArray[1]}" # for bash/ksh; for zsh, echo $myArray[2]
second element
You can get all the elements via "${myArray[#]}". You can use the slice notation ${array[#]:start:length} to restrict the portion of the array referenced, e.g. "${myArray[#]:1}" to leave off the first element.
The length of the array is ${#myArray[#]}. You can get a new array containing all the indexes from an existing array with "${!myArray[#]}".
Older versions of ksh before ksh93 also had arrays, but not the parenthesis-based notation, nor did they support slicing. You could create an array like this, though:
set -A myArray -- first "second element" 3rd
You can try of the following type :
#!/bin/bash
declare -a arr
i=0
j=0
for dir in $(find /home/rmajeti/programs -type d)
do
arr[i]=$dir
i=$((i+1))
done
while [ $j -lt $i ]
do
echo ${arr[$j]}
j=$((j+1))
done
An array can be loaded in twoways.
set -A TEST_ARRAY alpha beta gamma
or
X=0 # Initialize counter to zero.
-- Load the array with the strings alpha, beta, and gamma
for ELEMENT in alpha gamma beta
do
TEST_ARRAY[$X]=$ELEMENT
((X = X + 1))
done
Also, I think below information may help:
The shell supports one-dimensional arrays. The maximum number of array
elements is 1,024. When an array is defined, it is automatically
dimensioned to 1,024 elements. A one-dimensional array contains a
sequence of array elements, which are like the boxcars connected
together on a train track.
In case you want to access the array:
echo ${MY_ARRAY[2] # Show the third array element
gamma
echo ${MY_ARRAY[*] # Show all array elements
- alpha beta gamma
echo ${MY_ARRAY[#] # Show all array elements
- alpha beta gamma
echo ${#MY_ARRAY[*]} # Show the total number of array elements
- 3
echo ${#MY_ARRAY[#]} # Show the total number of array elements
- 3
echo ${MY_ARRAY} # Show array element 0 (the first element)
- alpha
If you want a key value store with support for spaces use the -A parameter:
declare -A programCollection
programCollection["xwininfo"]="to aquire information about the target window."
for program in ${!programCollection[#]}
do
echo "The program ${program} is used ${programCollection[${program}]}"
done
http://linux.die.net/man/1/bash "Associative arrays are created using declare -A name. "
There are multiple ways to create an array in shell.
ARR[0]="ABC"
ARR[1]="BCD"
echo ${ARR[*]}
${ARR[*]} prints all elements in the array.
Second way is:
ARR=("A" "B" "C" "D" 5 7 "J")
echo ${#ARR[#]}
echo ${ARR[0]}
${#ARR[#]} is used to count length of the array.
To read the values from keybord and insert element into array
# enter 0 when exit the insert element
echo "Enter the numbers"
read n
while [ $n -ne 0 ]
do
x[$i]=`expr $n`
read n
let i++
done
#display the all array elements
echo "Array values ${x[#]}"
echo "Array values ${x[*]}"
# To find the array length
length=${#x[*]}
echo $length
A Simple way :
arr=("sharlock" "bomkesh" "feluda" ) ##declare array
len=${#arr[*]} #determine length of array
# iterate with for loop
for (( i=0; i<len; i++ ))
do
echo ${arr[$i]}
done
In ksh you do it:
set -A array element1 element2 elementn
# view the first element
echo ${array[0]}
# Amount elements (You have to substitute 1)
echo ${#array[*]}
# show last element
echo ${array[ $(( ${#array[*]} - 1 )) ]}

Count how many times a strings appear in random generator

I have a while loop with an string array and a simple randomize for them.
My problem however is to count how many times the same strings have appear when the loop was running.
Ex :
oc/open string has appeared 3 times
rw/read string has appeared 2 times
oc/close string has appeared 3 times
etc....
At the moment im using if else methods inside the loop, but there must be a better way to count them? Any tips?
function injection {
COUNTER=0
countopen=0
while [ $COUNTER -lt 10 ]; do
module[0]="oc/open"
module[1]="oc/close"
module[2]="rw/read"
module[3]="rw/write"
randModule=$[$RANDOM % ${#module[#]}]
export MODULE=${module[$randModule]}
echo $MODULE
if [ $randModule == 0 ]; then
let countopen++
#let countclose++
#etc
#etc
fi
let COUNTER++
done
echo "Open $countopen"
}
injection
If you can use external commands, use sort and uniq to count the occurrences:
#!/bin/bash
module=( oc/open oc/close rw/read rw/write )
for i in {1..100} ; do
echo ${module[RANDOM % ${#module[#]}]}
done | sort | uniq -c
You can also count them yourself in an associative array:
#!/bin/bash
module=( oc/open oc/close rw/read rw/write )
declare -A count
for i in {1..100} ; do
mod=${module[RANDOM % ${#module[#]}]}
(( ++count[$mod] ))
done
for m in "${module[#]}" ; do
printf '%s %d\n' "$m" "${count[$m]}"
done

bash loop through list of numbers except given number

to loop through a continous list of numbers in bash I can do
for s in $(seq 1 5);do
echo ${s}
done
to loop through a continous list of numbers leaving a given number out in python I can do:
list = [s2 for s2 in range(6)[1:] if s2 != s1]
for s1 in list:
print s1
where list contains all numbers in range except s1
How do I do the same in bash?
Just use continue to skip this step:
for s in {1..5} # note there is no need to use $(seq...)
do
[ "$s" -eq 3 ] && continue # if var is for example 3, jump to next loop
echo "$s"
done
This returns:
1
2
4 # <--- 3 is skipped
5
From Bash Reference Manual → 4.1 Bourne Shell Builtins:
continue
continue [n]
Resume the next iteration of an enclosing for, while, until, or select
loop. If n is supplied, the execution of the nth enclosing loop is
resumed. n must be greater than or equal to 1. The return status is
zero unless n is not greater than or equal to 1.
Add a short circuit evaluation, || (logical OR) :
for s in $(seq 1 5); do
(( s == 3 )) || echo "$s"
done
(( s == 3 )) checks if $s is equal to 3, if not (||) echo the number.
With the reverse check ($s not equal to 3) and logical AND (&&):
for s in $(seq 1 5); do
(( s != 3 )) && echo "$s"
done
The classic way, if with test ([), non-equity test:
for s in $(seq 1 5); do
if [ "$s" -ne 3 ]; then
echo "$s"
fi
done
Reverse test, equity check:
for s in $(seq 1 5); do
if [ "$s" -eq 3 ]; then
continue
fi
echo "$s"
done
continue will make the loop control to go at the top rather than evaluating the following commands.
There is also a bash keyword [[ which behaves similarly in most cases but more robust.
You can use BASH arithmetic construct ((...)) like this:
s1=3 # skip this
s2=6 # upper count
for ((i=1; i<s2; i+=(i==s1-1?2:1) )); do echo $i; done
1
2
4
5
About: i+=(i==s1-1?2:1)
In the for loop instead of always incrementing i by 1 here we are incrementing i by 2 when i is 1 less then the number to be skipped.
Alternatively solution using BASH array:
arr=({1..5}) # populate 1 to 5 in an array
unset arr[s1-1] # delete element s1-1
# print the array
printf "%s\n" "${arr[#]}"
1
2
4
5

Bash Script accepting a number, then printing a set of int from 0 to the number entered

I am trying to write a bash script that accepts a number from the keyboard, and then prints a set of integers from 0 to the number entered. I can't figure out how to do it at all.
This is my code:
while [ 1 ]
do
echo -n "Enter a color: "
read user_answer
for (( $user_answer = $user_answer; $user_answer>0; $user_answer--))
echo $user_answer
fi
done
exit
The error I'm recieving is:
number_loop: line 10: syntax error near unexpected token echo'
number_loop: line 10: echo $user_answer'
Assign a separate variable in order to use increment/decrement operators. $user_answer=$user_answer will always be true and it will throw an error when trying to use decrement. Try the following :
#!/bin/bash
while [ 1 ]
do
echo -n "Enter a color: "
read user_answer
for (( i=$user_answer; i>0; i-- ))
do
echo $i
done
done
exit
You missed the do statement between your for and the echo.
bash has many options to write numbers. What you seem to be trying to do is easiest done with seq:
seq $user_answer -1 0
If you want to use your loop, you have to insert a ; do and replace the fi with done, and replace several $user_answer:
for (( i = $user_answer; i>0; i--)); do
echo $i
done
(btw: I assumed that you wanted to write the numbers in reverse order, as you are going backwards in your loop. Forwards is even easier with seq:
seq 0 $user_input
)
This is where a c-style loop works particularly well:
#!/bin/bash
for ((i = 1; i <= $1; i++)); do
printf "%s\n" "$i"
done
exit 0
Example
$ bash simplefor.sh 10
1
2
3
4
5
6
7
8
9
10
Note: <= is used as the for loop test so it 10 it iterates 1-10 instead of 0-9.
In your particular case, iterating from $user_answer you would want:
for (( i = $user_answer; i > 0; i--)); do
echo $i
done
The for loop is a bash internal command, so it doesn't fork a new process.
The seq command has a nice, one-line syntax.
To get the best of the twos, you can use the { .. } syntax:
eval echo {1..$answer}

Resources