Breaking out of a nested loop in bash - bash

How do you break out of a nested loop in bash?
Tried continue and break. break worked. But want to learn more.
for i in 1 2 3; do
if [[ $flag -eq 1 ]]; then
break
fi
done
How does break actually know the loop is nested?
Instead of break, can I use i=4 or something out of range to exit from a loop.

Use break followed by a number, to break out of that many levels of nesting. Example:
for i in 1 2 3 4 5; do
echo
echo i = $i
for j in 1 2 3 4 5; do
echo j = $j
if [ $j -eq 4 ]; then break; fi
if [ $j -eq 3 ] && [ $i -eq 4 ]; then break 2; fi
done
done
Result:
i = 1
j = 1
j = 2
j = 3
j = 4
i = 2
j = 1
j = 2
j = 3
j = 4
i = 3
j = 1
j = 2
j = 3
j = 4
i = 4
j = 1
j = 2
j = 3

Related

in Bash: how to echo alphabet in multiple lines from A to E then in a new line from F to J and so on

I have tried
declare -a arr=( {A..Y} )
for j in {06..10}; do
echo -n "$j "
for (( i=0; i<25; i++ )); do
if [ $i -eq 4 ]; then
echo -n "${arr[$i]}${arr[$i]}"
break
else
echo -n "${arr[$i]}${arr[$i]}"
echo -n " "
fi
done
echo
done
but I get
enter image description here
Also tried
declare -a arr=( {A..Y} )
for j in {06..10}; do
echo -n "$j "
for (( i=0; i<25; i++ )); do
echo -n "${arr[$i]}"
echo -n " "
done
echo
done
But I get
enter image description here
Thank you
You can use a bash array slice:
declare -a arr=( {A..Y} )
n=5
for ((i = 0; i < ${#arr[#]}; i = i + n))
do
echo "${arr[#]:i:n}"
done
A B C D E
F G H I J
K L M N O
P Q R S T
U V W X Y
Alternate method using common 'nix tools and no shell loop:
#!/usr/bin/env bash
printf '%s %s %s %s %s\n' {A..Y} | nl -nrz -s' ' -v6 -w2
Explanations:
printf '%s %s %s %s %s\n' {A..Y}: Prints formatted 5 string arguments %s separated by a space and ending with a new-line \n.
As the printf consumes only 5 arguments at a time out of the 25 generated by the Bash's bracket expression {A..Y}, the format will be applied to each group of 5 arguments, resulting in this output:
A B C D E
F G H I J
K L M N O
P Q R S T
U V W X Y
| nl -nrz -s' ' -v6 -w2: Streams the above to the nl command to add line numbers.
-nrz: Formats numbers right-aligned with zero padding.
-s' ': separates numbering from text with a blank .
-v6: Starts numbering at 6.
-w2: Number width 2.
Final result:
06 A B C D E
07 F G H I J
08 K L M N O
09 P Q R S T
10 U V W X Y
I think it's simpler if you keep a separate variable to count how many you've printed already and print a newline when you've printed 6 characters.
Example:
#!/bin/bash
declare -a arr=( {A..Y} )
count=0;
for ch in "${arr[#]}"
do
echo -n "$ch "
(( count = (count + 1) % 5 ))
if (( count == 0 )) ; then
# 6 characters printed, now print a newline
echo
fi
done
An alternative using an index variable in the loop instead:
for ((idx = 0; idx < ${#arr[#]}; ++idx)); do
echo -n "${arr[$idx]} "
if (( (idx+1) % 5 == 0 )); then
echo
fi
done
Both output:
A B C D E
F G H I J
K L M N O
P Q R S T
U V W X Y
If you want 06...10 to be printed first on each line:
step=5
for ((idx = 0; idx < ${#arr[#]}; ++idx)); do
if (( (idx) % step == 0 )); then
(( pre = idx/step + 6 ))
printf "%02d " "$pre"
fi
echo -n "${arr[$idx]} "
if (( (idx+1) % step == 0 )); then
echo
fi
done
Or simpler
step=5
for ((idx = 0; idx < ${#arr[#]}; idx += step)); do
(( pre = idx/step + 6 ))
printf "%02d " "$pre"
echo "${arr[#]:idx:step}"
done
Output:
06 A B C D E
07 F G H I J
08 K L M N O
09 P Q R S T
10 U V W X Y
Also
printf '%s\n' {A..Y} | paste -d ' ' - - - - -
Or with plain bash
alphabet=({A..Y})
for i in "${!alphabet[#]}"; do
((i % 5 == 4)) && sep=$'\n' || sep=' '
printf '%s%s' "${alphabet[i]}" "$sep"
done

Shell output to text file

So, I have the following shell:
#!/bin/bash
for (( i = 1; i <= 6; i++ ))
do
printf in_port=$i,actions=
for (( j = 1 ; j <= 6; j++ ))
do
if [ $i != $j ]; then
printf output:$j,
fi
done
printf "\n"
done
Which, produces the following output:
home#mininet:~$ ./hostsScript.sh
in_port=1,actions=output:2,output:3,output:4,output:5,output:6,
in_port=2,actions=output:1,output:3,output:4,output:5,output:6,
in_port=3,actions=output:1,output:2,output:4,output:5,output:6,
in_port=4,actions=output:1,output:2,output:3,output:5,output:6,
in_port=5,actions=output:1,output:2,output:3,output:4,output:6,
in_port=6,actions=output:1,output:2,output:3,output:4,output:5,
How would I go about appending each line of this output to a txt file, line-by-line?
option 1 inside the script:
#!/bin/bash
for (( i = 1; i <= 6; i++ ))
do
printf in_port=$i,actions=
for (( j = 1 ; j <= 6; j++ ))
do
if [ $i != $j ]; then
printf output:$j,
fi
done
printf "\n"
done >> output_file
option 2 inside the script:
#!/bin/bash
exec >> output_file
for (( i = 1; i <= 6; i++ ))
do
printf in_port=$i,actions=
for (( j = 1 ; j <= 6; j++ ))
do
if [ $i != $j ]; then
printf output:$j,
fi
done
printf "\n"
done
option 3 in run command:
./hostsScript.sh >> output_file

Bash script - a puzzle

The script takes only one argument (call it n). It then alternately adds and subtracts each digit of the numbers from 1 through n.
For example:
n = 12
1 - 2 + 3 - 4 + 5 - 6 + 7 - 8 + 9 - 1 + 0 - 1 + 1 - 1 + 2
output: 5
#!/bin/bash
number=$1
result=1
if (( number == 1 || number == 0 )); then
echo
echo number: $number
echo
else
for (( i = 1; i < number; i++ )); do
if (( i < 10 )); then
if (( i % 2 == 0 )); then
let "result = result + i + 1"
else
let "result = result - i - 1"
fi
else
for (( i = 10; i <= number; i++ )); do
if (( i < 100 && i >= 10)); then
let "result = result - i/10 + i%10"
else
let "result = result - i/100 + (i%100)/10 - (i/100)%10"
fi
done
fi
done
echo
echo result: $result
echo number: $number
echo
fi
It works fine for ints 0 to 9, but for n>9 it gives the wrong answer.
Please correct my script and explain to me why it's behaving like this.
PS I'm new to bash scripting, so I'd love to see another solution to this puzzle
PPS I'm familiar to java
EDIT
I fixed the code, and it works correct now
#!/bin/bash
number=$1
result=1
if (( number == 1 || number == 0 )); then
echo
echo number: $number
echo
else
for (( i = 1; i < number; i++ )); do
if (( i < 9 )); then
if (( i % 2 == 0 )); then
let "result = result + i + 1"
else
let "result = result - i - 1"
fi
fi
done
for (( i = 10; i <= number; i++ )); do
if (( i < 100 )); then
let "result = result - i/10 + i%10"
else
if (( i % 2 == 0 )); then
let "result = result - i/100 + (i%100)/10 - (i%100)%10"
else
let "result = result + i/100 - (i%100)/10 + (i%100)%10"
fi
fi
done
echo
echo result: $result
echo number: $number
echo
fi
Ok this should work apologies that I miss-read the question earlier
number=$1
MATSTR="";
for((i=1; i <= number; i++)); do
MATSTR="$MATSTR$i";
done;
echo $MATSTR | sed -e 's/\(.\)\(.\)/\1-\2+/g' | sed 's/+$//' | bc
Explanation:
For loop to create string concatenating all numbers from 1 to the number.
sed statement replaces the every two digits with 1st digit minus second digit plus
sed removes any trailing plus
bc calculates the statement
Here's your original solution, but with bug fixes (in comments). It still won't work for number > 99, of course.
#!/bin/bash
number=$1
result=0 # Don't start at 1!!
if (( number == 1 || number == 0 )); then
echo
echo number: $number
echo
else
for (( i = 1; i <= number; i++ )); do # <=, not <
if (( i < 10 )); then # < 10, not < 9
if (( i % 2 == 0 )); then
let "result = result - i" # subtraction! Also, the +1 was unnecessary
else
let "result = result + i" # addition!
fi
else
if (( i < 100 && i >= 10)); then
let result=result-i/10+i%10
else
let "result=result-i/100+(i%100)/10-(i/100)%10"
fi
fi
done
echo
echo result: $result
echo number: $number
echo
fi
After misreading the question, (or the question being unclear), the answer took only minor adjustments. The sting solution is probably the simplest approach using modulo to togget +/-:
#!/bin/bash
declare -i num=$1
declare -i res=0
str=""
[ "$num" -gt 1 ] || {
printf "\n Error: invalid input. usage: %s int (greater than 1)\n\n" "${0//*\//}"
exit 1
}
for ((i = 1; i <= $num; i++)); do # create the string of digits
str="${str}${i}"
done
printf "\n Calculations:\n\n"
for ((i = 0; i < ${#str}; i++)); do # walk down the string adding/subtracting each digit
if [ $((i % 2)) -eq 0 ]; then
((res+=${str:$i:1}))
printf " res + %3s = %3s\n" "${str:$i:1}" "$res"
else
((res-=${str:$i:1}))
printf " res - %3s = %3s\n" "${str:$i:1}" "$res"
fi
done
printf "\n Final Result: %s\n\n" "$res"
exit 0
output:
$ ./puzzle.sh 12
Calculations:
res + 1 = 1
res - 2 = -1
res + 3 = 2
res - 4 = -2
res + 5 = 3
res - 6 = -3
res + 7 = 4
res - 8 = -4
res + 9 = 5
res - 1 = 4
res + 0 = 4
res - 1 = 3
res + 1 = 4
res - 1 = 3
res + 2 = 5
Final Result: 5

Numerical if statement in a Bash Script using $'s

I have written a short Bash Script:
for j in 0 1 2 3 4 5
do
for (( i=$j; i <= 5; i++ ));
do
if [ $(($(($i - $j)) > 1)) ]; then
echo "True"
else
echo "False"
fi
done
done
I expect this script to output a mixture of Trues and Falses however it only outputs Trues. I have checked and seen that $(($(($i - $j)) > 1)) is correctly producing 0s and 1s as it is supposed to but apparently the if statement is not registering these and always assumes "True".
Am I missing something in my code?
Thank you very much
James
Your script is checking if [ 0 ] and if [ 1 ] which isn't what you think. It will always return true because it is checking that the length of the string 0 and 1 is not zero.
Change it to:
for j in {0..5}
do
for (( i=j; i<=5; i++ ))
do
if (( i - j > 1 ))
then
echo True
else
echo False
fi
done
done
Note that the use of the $ prefix for variables within ((...)) is optional.
You're using if incorrectly, you're testing for string length when you want an arithmetic comparison:
for j in {0..5}; do
for i in $(seq $j 5); do
if (( i - j > 1 )); then
echo "True"
else
echo "False"
fi
done
done

Bash script error: [i: command not found

This program is suppose to accept a number from the user and print that many prime numbers. For some reason the program doesn't work. I am new to bash scripting and this is my first program. To my eyes everything seems to be correct. Please tell me where I went wrong.
echo Enter num
read n
i=2
j=2
for(( i=2; i <= n; i++ ))
do
for(( j=2; j < i-1; j++ ))
do
if [i % j == 0];
then
break
fi
if [i == j];
then
echo " $i"
fi
done
done
This is the output I get
Enter num
20
prime.sh: line 12: [i: command not found
prime.sh: line 18: [i: command not found
prime.sh: line 12: [i: command not found
prime.sh: line 18: [i: command not found
.
.
.
After making the suggested changes
read -p "Enter a number : " n
i=2
j=2
for(( i=2; i <= n; i++ ))
do
for(( j=2; j <= i-1; j++ ))
do
if [ $(( i % j )) == 0 ]
then
break
fi
if [ i == j ]
then
echo " $i"
fi
done
done
I was able to run the program but it didn't produce any result
http://i.stack.imgur.com/Fd1se.png
You need to place a space after the [ because [ is an application.
And you can't make calculations between the brackets. You will need to tell bash it needs to calculate the values. So you would need to change line 11 to if (( i % j == 0 )).
if [i % j == 0]; Should be if [ i % j == 0 ];
Same for the one on line 18
Like the others have said, the [ program needs a space before its parameters, like all programs need a space before their args. This is one reason why I prefer the test builtin to [ with its kludgy syntax requiring a useless ] as the last arg just to make it look pretty and sane.
if test $((i % j)) = 0; then
break
fi
#!/bin/bash
#
# primes
#
read -p "Enter a number: " n
i=2
for (( i=2; i <= n; i++ ))
do
for (( j=2; j*j < i; j++ ))
do
if ((i % j == 0))
then
echo "no prime, $i divider: "$j
break
fi
done
done
updated, after realizing (thanks Will Ness), that all primes up to INPUT are searched.
i needs to run to √(n) only.

Resources