bash - for loop different iterations - bash

I know how to do sequential for loops like:
for i in $(seq 0 63)
do
echo $i
done
This would print 0-63 .
But what if I wanted certain numbers looped like only 0, 5, 25, 43, 44, 51, 54.
I know I could do the following:
for i in $(seq 0 63)
do
if [ "$i" -eq 0 ] || [ "$i" -eq 5 ] || [ "$i" -eq 25 ] || [ "$i" -eq 43 ] || [ "$i" -eq 44 ] || [ "$i" -eq 51 ] || [ "$i" -eq 54 ]; then
echo $i
fi
done
But the result will be that it still goes through those loop iterations, and is not an efficient solution.
Is there something I can use with seq to describe these certain numbers 0,5,25,43,44,51,54?

If you want to iterate over a known list, you can simply store the elements, space-separated, in a variable and loop over that:
nums="0 5 25 43 44 51 54";
for i in $nums;
do echo $i;
done;

Related

Transforming IF statements to FOR or WHILE (Shell Script)

The following code is displaying a Progress Bar on an HTML page. It runs fine but takes up far to many lines of code.
QUESTION:
- How would I go about transforming this into a for or while loop, replicating its functionality?
if [ $per_usage -ge 1 ] && [ $per_usage -le 10 ]
then
indg="|"
indb=""
indr=""
elif [ $per_usage -gt 10 ] && [ $per_usage -le 20 ]
then
indg="||"
indb=""
indr=""
elif [ $per_usage -gt 20 ] && [ $per_usage -le 30 ]
then
indg="|||"
indb=""
indr=""
elif [ $per_usage -gt 30 ] && [ $per_usage -le 40 ]
then
indg="||||"
indb=""
indr=""
elif [ $per_usage -gt 40 ] && [ $per_usage -le 50 ]
then
indg="|||||"
indb=""
indr=""
elif [ $per_usage -gt 50 ] && [ $per_usage -le 60 ]
then
indg="|||||"
indb="|"
indr=""
elif [ $per_usage -gt 60 ] && [ $per_usage -le 70 ]
then
indg="|||||"
indb="||"
indr=""
elif [ $per_usage -gt 70 ] && [ $per_usage -le 80 ]
then
indg="|||||"
indb="|||"
indr=""
elif [ $per_usage -gt 80 ] && [ $per_usage -le 90 ]
then
indg="|||||"
indb="||"
indr="||"
elif [ $per_usage -gt 90 ]
then
indg=""
indb=""
indr="||||||||||"
else
indg=""
indb=""
indr=""
fi
For example my output is like if per_usage value is 41
41 % |||||
Thank you in advance.
This sort of thing can be looped pretty easily:
#!/bin/bash
get_string () {
per_usage="$1"
if [ "$per_usage" -le 100 ] && [ "$per_usage" -ge 0 ]; then
echo -en "${per_usage}%\t"
bars=$(($per_usage / 10 + 1))
printf "%0.s|" $(seq 1 $bars)
echo
fi
}
i=0
while [ "$i" -le 100 ]; do
string=$(get_string "$i")
echo "$string"
let i++
done
In this example, the get_string function can be used to generate a string based on an input number. For instance, get_string 41 will print 41% |||||. In the little while loop below, the string for 0 - 100 is stored in the $string variable and printed.
bars stores the number of bars to print. Just one bar per 10%. Then printf and seq are used to print a | bars number of times.
Hopefully with this, you'll be on the right track to cleaning up your code.

Find neighbouring prime numbers using BASH

I had a pretty hard homework (hard for beginners like me).
We had to find given number's nearest neighbouring primes.
User input must be a two-digit number and can be prime. The results must be printed on screen, seperated by whitespaces.
For input validaton:
while read -p "Give me a two-digit numer: " n && [[ -z $n ]] || [[ ! $n =~ ^[0-9]+$ ]] || [ $n -lt 10 ] || [ $n -gt 99 ]
do
echo -e "\e[31mLet's try that again ...\e[0m"
done
I was thinking about using an already initialized array, containing the prime numbers from 7 to 101 and somehow compare it with the user input.
# Initialize an array with possible prime numbers
primes=(7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101)
# Create an inverted version of 'primes' array ... because of reasons ...
for (( i="${#primes[#]}-1" ; i>=0 ; i-- ));
do
rprimes+="${primes[i]} "
done
Who else likes 'for' loops?
# To find the next prime number
i=0
for i in ${primes[#]}
do
if [ $i -gt $n ]
then
gtn=$i
break
fi
done
# To find the previous prime number in the inverted 'rprimes' array
for i in ${rprimes[#]}
do
if [ $i -lt $n ]
then
ltn=$i
break
fi
done
# Results:
echo "$ltn $n $gtn"
The script works, but I would really like to know, if there is a way to find the 'previous' value without the need to create an inverted array.
Sorry for mah english!
Solution/Edit:
for (( i=0; for (( i=0; i<=${#primes[#]}; i++ ));
do
if [ ${primes[i]} -lt $n ] && [ ${primes[i+1]} -ge $n ]
then
ltn=${primes[i]}
break
fi
done
You already understand for loops clearly, since you use one to create the reversed array rprimes. Just run a forward for on the primes array. Isn't the condition for ltn=${primes[i]} just
[ ${primes[i]} -lt $n ] && [ ${primes[i+1]} -ge $n ]
?
And the condition for gtn=${primes[i+1]} likewise
[ ${primes[i]} -le $n ] && [ ${primes[i+1]} -gt $n ]
?
Just find the index of the prime you're interested in, it's then easy to look around just by ±1:
for (( i=0; i<${#primes[#]}; i++ )) ; do
if (( n <= primes[i] )) ; then
p=$(( n == primes[i] ))
echo ${primes[i-1]} $n ${primes[i+p]}
break
fi
done

to many arguments in while loop bash

I'm new in bashscripting and I can´t find my failure.
Errorcode:
[: too many arguments
Script:
i=5
while [ "${array[i]}" >= "256" ] -a [ "$i" > "0" ]; do
array[$i]=0
i=$(( $i - 1 ))
array[$i]=$((${array[$i]}+1))
done
I guess you are trying to do some base 256 carrying. Here is the corrected and simplified script, with an added example:
#!/usr/bin/env bash
i=5
array=(254 255 255 255 255 256)
while [ ${array[i]} -ge 256 ] && [ $i -gt 0 ]; do
array[i]=0
(( i-- ))
(( array[i]++ ))
done
echo ${array[#]}
Output:
255 0 0 0 0 0
Honestly I would rather use Bash's arithmetic expansion instead of test, so [ ${array[i]} -ge 256 ] && [ $i -gt 0 ] can instead be
(( array[i] >= 256 && i > 0 ))

Shell Script Too Many Arguments for if condition

My current script does the following;
It takes integer as a command line argument and starts from 1 to N , it checks whether the numbers are divisible by 3, 5 or both of them. It simply prints out Uc for 3, Bes for 5 and UcBes for 3,5. If the command line argument is empty, it does the same operation but the loop goes to 1 to 20.
I am having this error "Too many arguments at line 11,15 and 19".
Here is the code:
#!/bin/bash
if [ ! -z $1 ]; then
for i in `seq 1 $1`
do
if [ [$i % 3] -eq 0 ]; then
echo "Uc"
elif [ i % 5 -eq 0 ]; then
echo "Bes"
elif [ i % 3 -eq 0 ] && [ i % 5 -eq 0 ]
then
echo "UcBes"
else
echo "$i"
fi
done
elif [ -z $1 ]
then
for i in {1..20}
do
if [ i % 3 -eq 0 ]
then
echo "Uc"
elif [ i % 5 -eq 0 ]
then
echo "Bes"
elif [ i % 3 -eq 0 ] && [ i % 5 -eq 0 ]
then
echo "UcBes"
else
echo "$i"
fi
done
else
echo "heheheh"
fi
Note that [ is actually synonym for the test builtin in shell (try which [ in your terminal), and not a conditional syntax like other languages, so you cannot do:
if [ [$i % 3] -eq 0 ]; then
Moreover, always make sure that there is at least one space between [, ], and the variables that comprise the logical condition check in between them.
The syntax for evaluating an expression such as modulo is enclosure by $((...)), and the variable names inside need not be prefixed by $:
remainder=$((i % 3))
if [ $remainder -eq 0 ]; then
You should probably use something like :
if [ $(($i % 3)) -eq 0 ]
instead of
if [ $i % 3 -eq 0 ]
if [ [$i % 3] -eq 0 ]
Your script could be greatly simplified. For example:
#!/bin/sh
n=0
while test $(( ++n )) -le ${1:-20}; do
t=$n
expr $n % 3 > /dev/null || { printf Uc; t=; }
expr $n % 5 > /dev/null || { printf Bes; t=; }
echo $t
done
gives slightly different error messages if the argument is not an integer, but otherwise behaves the same.

Problem with logical operator in bash script

I read in tldp.com that
if [ $condition1 ] && [ $condition2 ]
Same as: if [ $condition1 -a $condition2 ]
Returns true if both condition1 and condition2 hold true..."
but when I tried
if [ $a == 2 ] || [ $b == 4 ]
then
echo "a or b is correct"
else
echo "a and b are not correct"
fi
it gives error. I'm using bash.
Your logic is ok but your comparison operators are incorrect, you should use the '-eq' for comparing integers and '==' for strings. See 'man test' for quick reference, though it's also documented in 'man bash'.
When using integer comparison it is always best to initialise variables to 0 as well otherwise if they remain unset you will get errors.
As mentioned by c00k, use [[ rather than [ if using bash as it is a builtin so bash will not need to shell out to use the /usr/bin/[ command.
i.e.
a=0;b=0
# do something else with a or b
if [[ $a -eq 2 ]] || [[ $b -eq 4 ]]
then
echo "a or b is correct"
else
echo "a and b are not correct"
fi
If you're using Bash, then drop the single [ and use double ones [[.
For arithmetic operations, use ((.
So you'd want to write this:
if (( a == 2 )) || (( b == 4 )); then
echo "foo"
fi # etc
Did you assign a value to a and b? If not you have to (otherwise you definitely should) quote your variables with double quotes:
if [ "$a" == "2" ] || [ "$b" == "4" ]; then
echo "a or b is correct";
else
echo "a and b are not correct";
fi
Correct me if I'm wrong. && and || are bash comparison
#!/usr/bin/ksh
set -x ###### debug mode on
while :
do
dt=`date '+%M'`
if ([ "$dt" -ge "15"] && [ "$dt" -le "17" ]) || ([ "$dt" -ge "25" ] && [ "$dt" -le "27" ])
then
echo "Time-->minutes between 15 to 17 OR 25 to 27"
else
echo "Time--> minutes out of range"
fi
sleep 300 ##### sleep for 5 minutes
done
------------- below lines are debug output
+ + date +%M
dt=17
+ [ 17 -ge 15 ]
+ [ 17 -le 17 ]
+ echo Time--> minutes between 15 to 17 OR 25 to 27
Time--> minutes between 15 to 17 OR 25 to 27
+ date
Sat Mar 3 15:17:23 AST 2012
+ sleep 300
>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ :
+ + date +%M
dt=22
+ [ 22 -ge 15 ]
+ [ 22 -le 17 ]
+ [ 22 -ge 25 ]
+ echo Time--> minutes out of range
Time--> minutes out of range
+ sleep 300
^C$ ######## breaking out of loop(terminating the execution).
$
$ env |grep -i shell
+ grep -i shell
+ env
SHELL=/bin/ksh
$ set +x ##### ending debug mode.

Resources