Dynamic beginning of for loop - bash

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

Related

BASH: loop array values unlimited

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).

Is it possible to break a for loop in bash?

I have the below scenario.
I'm iterating through array elements in bash and comparing the values.
#!/bin/bash
for (( i = 0 ; i <=m-1 ; i++));
do for (( j = 0 ; j <=n-1 ; j++));
do
if [ "${sourcedisktype[i]}" == "SSD" -a "${targetdisktype[j]}" == "BSAS" ];
then echo "volume cannot be moved1";
elif [ "${sourcedisktype[i]}" == "SSD" -a "${targetdisktype[j]}" == "SAS" ];
then echo "volume cannot be moved2";
elif [ "${sourcedisktype[i]}" == "SAS" -a "${targetdisktype[j]}" == "BSAS" ];
then echo "volume cannot be moved3";else
echo "Volume can be moved for this combination of disk type";done;done;
I don't want to execute the else part even if one elif fails. Is there any way to do it. Is it possible to use break statement after elif?
Of course you can break out of your loops in bash, you can actually even break out of N nested loops if you use the statement break N where N is an integer >=1.
For more details please have a look at the documentation:
http://tldp.org/LDP/abs/html/loopcontrol.html
You can actually also use continue N but it can be quite tricky and hard for others to understand, so I would not recommend to use it.
PS: do not forget to indent your code properly in order for others to understand it quickly and even for yourself, I can affirm that it will be a pain if you try to reread your code in a couple of months/years. You will regret not having indented/commented it properly.

Shell script for word in $sentece How to change the value of word?

I need some help from this awesome community.
I'm trying to write a script that loops through each word of a sentence stored in a variable (for example SENTENCE).
Example:
for WORD in $SENTENCE
do
echo do something
done
The problem that I'm facing is that I need to change the value of WORD to restart the loop if a certain condition is true inside the loop.
Example:
for WORD in $SENTENCE
do
echo do something
if [[ $SOMETHING_HAPPENED == TRUE ]]; then
WORD=$FIRST_WORD_IN_SENTENCE
fi
done
Basically, I need to restart the loop if certain conditions are met (SOMETHING_HAPPENED), but I don't know how to do this properly.
If this was a normal C loop I would do it like this:
for (i=0;i<10;i++){
do_something();
if (SOMETHING_HAPPENED == TRUE){
i=0;
}
}
How do I do this in shell script?
Thank you.
You could use a loop around your for loop that executes the for loop until the something doesn't happen:
repeat=yes
while [ "$repeat" = yes ]; do
repeat=no
for WORD in $SENTENCE; do
# do something
if [[ $SOMETHING_HAPPENED == TRUE ]]; then
repeat=yes
break
fi
done
done
while [[ 1 ]]; do # infinite loop
for word in $sentence; do
…
if [[ $condition ]]; then
continue 2 # go to the next iteration of the *outer* loop
fi
done
break # escape the outer loop
done
To restart the loop you can use BASH arrays and run your loop like this:
arr=( $sentence )
for ((i=0; i<${#arr[#]}; i++)); do
word="${arr[$i]}"
# evaluate condition
if [[ "$SOMETHING_HAPPENED" == TRUE ]]; then
i=0
continue
fi
echo "$word"
done

Recursive Fibonacci in Bash script

This is my attempt:
#!/bin/bash
function fibonacci(){
first=$1
second=$2
if (( first <= second ))
then
return 1
else
return $(fibonacci $((first-1)) ) + $(fibonacci $((second-2)) )
fi
}
echo $(fibonacci 2 0)
I think i'm having trouble with the else statement. I get the error return: +: numeric argument required
on line 14.
The other problem that i'm having is that the script doesn't display any numbers even if i do echo $(fibonacci 0 2). I thought it would display a 1 since i'm returning a 1 in that case. Can someone give me a couple of tips on how to accomplish this?
After checking out some of your answers this is my second attempt.. It works correctly except that it displays the nth fibonacci number in the form 1+1+1+1 etc. Any ideas why?
#!/bin/bash
function fibonacci(){
first=$1
second=$2
if (( first <= second ))
then
echo 1
else
echo $(fibonacci $((first-1)) ) + $(fibonacci $((first-2)) )
fi
}
val=$(fibonacci 3 0)
echo $val
My final attempt:
#!/bin/bash
function fibonacci(){
first=$1
if (( first <= 0 ))
then
echo 1
else
echo $(( $(fibonacci $((first-1)) ) + $(fibonacci $((first-2)) ) ))
fi
}
val=$(fibonacci 5)
echo $val
thanks dudes.
#!/bin/bash
function fib(){
if [ $1 -le 0 ]; then
echo 0
elif [ $1 -eq 1 ]; then
echo 1
else
echo $[`fib $[$1-2]` + `fib $[$1 - 1]` ]
fi
}
fib $1
The $(...) substitution operator is replaced with the output of the command. Your function doesn't produce any output, so $(...) is the empty string.
Return values of a function go into $? just like exit codes of an external command.
So you need to either produce some output (make the function echo its result instead of returning it) or use $? after each call to get the value. I'd pick the echo.
As Wumpus said you need to produce output using for example echo.
However you also need to fix the recursive invocation.
The outermost operation would be an addition, that is you want:
echo $(( a + b ))
Both a and b are substitutions of fibonacci, so
echo $(( $(fibonacci x) + $(fibonacci y) ))
x and y are in turn arithmetic expressions, so each needs its own $(( )), giving:
echo $(( $(fibonacci $((first-1)) ) + $(fibonacci $((second-2)) ) ))
If you are confused by this, you should put the components into temporary variables and break down the expression into parts.
As to the actual fibonacci, it's not clear why you are passing 2 arguments.
Short version, recursive
fib(){(($1<2))&&echo $1||echo $(($(fib $(($1-1)))+$(fib $(($1-2)))));}
While calculating fibonacci numbers with recursion is certainly possible, it has a terrible performance. A real bad example of the use of recursion: For every (sub) fibonacci number, two more fibonacci numbers must be calculated.
A much faster, and simple approach uses iteration, as can be found here:
https://stackoverflow.com/a/56136909/1516636

Loop first run continue in shell script

How to break out of infinite while loop in a shell script?
I want to implement the following PHP code in shell script(s):
$i=1;
while( 1 ) {
if ( $i == 1 ) continue;
if ( $i > 9 ) break;
$i++;
}
break works in shell scripts as well, but it's better to check the condition in the while clause than inside the loop, as Zsolt suggested. Assuming you've got some more complicated logic in the loop before checking the condition (that is, what you really want is a do..while loop) you can do the following:
i=1
while true
do
if [ "$i" -eq 1 ]
then
continue
fi
# Other stuff which might even modify $i
if [ $i -gt 9 ]
then
let i+=1
break
fi
done
If you really just want to repeat something $count times, there's a much easier way:
for index in $(seq 1 $count)
do
# Stuff
done
i=1
while [ $i -gt 9 ] ; do
# do something here
i=$(($i+1))
done
Is one of the ways you can do it.
HTH

Resources