I would like to sequence through a set of numbers.
A simple example is sequence through 2 numbers, then sleep for 5 seconds before proceeding to the next 2.
For example:
1
2
(sleeping for 5 seconds)
3
4
(sleeping for 5 seconds)
Thank you in advance
You can use this for loop in bash:
for ((i=1; i<10; i+=2)); do echo $i $((i+1)); sleep 5; done
1 2
3 4
5 6
7 8
9 10
You could do this as well, using the modulo operator %:
declare -i max_value=10 # set it whatever you want
for ((i = 1; i < $max_value; i++)); do
printf "Number is: $i\n"
[[ $(( $i % 2 )) == 0 ]] && printf "Sleeping\n" && sleep 5 # sleep when the number is even
# do whatever...
done
Output:
Number is: 1
Number is: 2
Sleeping
Number is: 3
Number is: 4
Sleeping
Number is: 5
Number is: 6
Sleeping
Number is: 7
Number is: 8
Sleeping
Number is: 9
If your set of numbers is not a simple set of ascending integers like anubhava's response, you can do similar things.
For example, if your sequence is in a list, list.txt:
declare -ai a=($(<list.txt)); ## Read the sequence from list.txt into a
declare -i i l=${#a[#]};
for ((i=0; i<l; i+=2)); do
echo ${a[i]} ${a[i+1]};
sleep 5;
done;
If your sequence's numbers are in a string, like s='1,2,3,4,5,6,4,32,25,4,6,4', you could do declare -ai a=(${s//,/ }) to fill a with your numbers.
Related
for i in {1..100}
do
if [ ($i % 2) -ne 0 ]
then
echo $i
fi
done
Hi! I am learning Bash but I have some probelems with printing the odd numbers in the range 1 to 100, obviously I have some syntax error which I cannot find.
The {x..y} construct allows a 3rd argument to designate the increment value (default is 1), eg:
for i in {1..20..3} # start with 1 and increment by 3 until you reach/pass 20
do
echo $i
done
This generates:
1
4
7
10
13
16
19
For odd vs even you designate the starting number and increment by 2:
# odd numbers
for i in {1..10..2} # start with an odd number and increment by 2
do
echo $i
done
1
3
5
7
9
# even numbers
for i in {2..10..2} # start with an even number and increment by 2
do
echo $i
done
2
4
6
8
10
I think your if statement isn't correct. Here's a small rewrite with a working if:
#!/bin/bash
for i in {1..100}
do
isEvenIfZero=$i%2;
if [[ $isEvenIfZero -ne 0 ]];
then
#echo -n $i #single line
echo $i
fi
done
I'm doing a script in bash to monitor different changes to a variable ( a number). I want to get , let's say 10 readings, and determine if the number is growing, remaining the same or getting lower. The readings will be like : 1-2-4-3-6-7-9-8-6-9-7.... my script needs to know if the number is increasing or decreasing.
Store the previous value in a variable and compare the values numerically:
#! /bin/bash
for n in 1 2 4 3 3 6 7 9 8 6 9 7 ; do
echo $n
if [[ $previous ]] ; then
if (( previous < n )) ; then
echo Growing
elif (( previous > n )) ; then
echo Getting lower
fi
fi
previous=$n
done
I am trying to get an automatic reverse counting with user input.
The error is I keep getting the first number in the sequence twice.
#!/bin/bash
#Print decremental numbers based on user input
#n...8 7 6 5 4 3 2 1
echo "Input number"
read k
while test $k != 0
do
echo "$k"
k="$(( k - 1 ))"
done
Output:
[root#localhost standalone_scripts]# . ./decr.sh
Input number
5
5
4
3
2
1
as is pointed out in the comments, it's displaying the user input. if you don't like the way it's displayed, then you can switch the order of the subtraction with the echo.
#!/bin/bash
#Print decremental numbers based on user input
#n...8 7 6 5 4 3 2 1
echo "Input number"
read k
while test $k != 0
do
k="$(( k - 1 ))"
echo "$k"
done
Here's an idiomatic Bash reformulation of the script:
Using read -p prints the prompt string and the user's input on the same line, which makes misinterpreting the user's input as being part of the script's output less likely (which is what prompted the OP's question).
This is probably preferable to using read -s, which suppresses echoing the user's input as it is being typed.
Using a Bash-native, C-style arithmetic for loop (for (( ...; ...; ... ))) avoids the concerns about portability of the non-standard external seq utility.
For small input numbers, this is probably also faster than using seq, and also gives you the flexibility to act on each number individually.
For large input numbers, seq will be faster.
#!/usr/bin/env bash
read -p "Input number: " k
for (( i = k; i >= 1; --i )); do
echo "$i"
done
You could avoid the newline (and maybe add a :) after asking for user input, so that it's clear that the first 5 is user input.
Then here is an example that uses seq instead of while, though according to #andlrc, seq is less portable.
#!/bin/bash
#Print decremental numbers based on user input
#n...8 7 6 5 4 3 2 1
printf "Input number: "
read k
seq $k -1 1
Output:
$ ./decr.sh
Input number: 5
5
4
3
2
1
Edit:
#mklement0's answer is best.
read -p "Input number: " k
The read -s will turn off the echo effect of read
#!/bin/bash
#Print decremental numbers based on user input
#n...8 7 6 5 4 3 2 1
echo "Input number"
read -s k
while test $k != 0
do
echo "$k"
k="$(( k - 1 ))"
done
Your formatting is broken:
echo "Input number"; read k; while test $k != 0; do
echo "$k"; k="$(( k - 1 ))"; done
Seems to work fine for me:
Input number
3 <- this is what you enter
3 <- this is the beginning of the loop
2
1
try
#!/bin/bash
#Print decremental numbers based on user input
#n...8 7 6 5 4 3 2 1
echo "Input number"
read k
seq $((k-1)) -1 1
alternatively
#!/bin/bash
#Print decremental numbers based on user input
#n...8 7 6 5 4 3 2 1
echo "Input number"
read k
while test $k -gt 1; do
k=$((k-1))
echo "$k"
done
Using for and seq is the most elegant:
for i in `seq 5 -1 1`; do
echo $i
done
Output:
5
4
3
2
1
I am trying to create a multiply table using a for loop but I don't know how to initialize the variables and if the read variable needs to be the same in the for loop
Syntax:
#!/bin/bash
#multiplication table
#$#=parameter given to the script
#$i=variable in for loop
#$1=variable represents a valid number not zero
#$0=variable represents bash script name
echo "Enter number you want to multiply"
read varnumber
echo "This number: $varnumber has this multiplication result:
if [ $varnumber -eq 0 ]
then
echo "Error - Number missing from command line argument"
echo "Syntax : $0 number"
echo "Use to print multiplication table for a given number"
exit 1
fi
n=$varnumber
for i in 1 2 3 4 5 6 7 8 9 10
do
echo "$varnumber * $i = `expr $i \* $varnumber`"
A for loop should end with done, so :
for i in 1 2 3 4 5 6 7 8 9 10
do
echo "$varnumber * $i = `expr $i \* $varnumber`"
done #line added
Also there is no harm in doing :
n="$varnumber"
and note that backticks (` `) is not preferred in bash. Use the command $() format instead for this reason. So :
echo "$varnumber * $i = $(expr $i \* $varnumber)" # Used $() syntax.
See what is obsolete in bash.
In fact, much better if you can finish the job without expr which is an external command:
echo "$varnumber * $i = $((i * varnumber))" # Faster than the previous version
(Thanks #benjamin-w for this suggestion)
A much simpler bash script to do the above goes like this:
#!/bin/bash
echo "Enter the number for which the multiplication table is desired."
read varname
if [ $varname -eq 0 ];
then
echo "Error!! The multiplication of anything with 0 results to 0."
exit 130 # Exit with CTRL + C
fi
for ((i=1; i<=10; i++)); # You can change 10 into any other number
# according to your requirement.
# Or substitute it with a variable (say N)
# which can be prompted to the user.
do
product=$(expr $i \* $varname) # Or use: `expr $i \* $varname`
echo "$varname times $i = $product"
done
The output will look like:
"Enter the number for which the multiplication table is desired."
If I enter 5 when prompted, the following will be printed:
5 times 1 = 5
5 times 2 = 10
5 times 3 = 15
5 times 4 = 20
5 times 5 = 25
5 times 6 = 30
5 times 7 = 35
5 times 8 = 40
5 times 9 = 45
5 times 10 = 50
so I'm dealing with column averaging problems
I have several columns of number,
1 2 3 4 5
2 3 4 5 6
8 4 5 6 7
9 8 7 6 5
1 9 9 9 2
What I want to do is to take an average of each column so that they will yield
4 5 5 6 5
The summing part is not a problem, I'm able to get the sum of each column. However, somehow I'm having trouble in counting the number of columns (variable $x)
Here is my code
while read line; do
x = 0
read -a array <<< "$line"
for i in "${!array[#]}"
do
column[${i}]=$((${column[${i}]}+${array[$i]}))
((x++))
done
done < $TMP
for sum in ${column[#]}
do
average=`expr $sum / $x`
remainder=`expr $sum % $x`
mult=`expr $remainder \* 10`
fracvalue=`expr $mult / $x`
echo $x
echo $average
echo $sum
echo $remainder
echo $mult
echo $fracvalue
done
The last several lines are for my testing purposes, here the $X keeps showing 1 instead of 5. That's why it screws up all the other variable. Does anybody know where's the flaw in this code? Really appreciate your help. Thanks
Another approach:
#!/bin/bash
declare -ia sum # declare array of integers
declare -i lines=0 # declare integer to count lines
# read file to array and sum values
while read -ra array; do
for ((i=0; i<${#array[#]};i++)); do
sum[$i]+=${array[$i]}
done
lines+=1
done < file
# print averages
for ((j=0; j<$i;j++)); do
echo -n "$((${sum[$j]}/$lines)) "
done
Output:
4 5 5 6 5
The problem is in the line
x = 0
should be
x=0 and make ((x+1)) as let "x+=1" it should work.
Maybe your file doesn't have the same number of lines in each column.
while read line; do
read -a array <<< "$line"
for i in "${!array[#]}"
do
column[${i}]=$((${column[${i}]}+${array[$i]}))
((x[${i}]++))
done
done < $TMP
for i in ${!column[#]}
do
sum=${column[$i]}
x=${x[${i}]}
#your calcul
done
The initial issue, aside from a few syntax peculiarities, was the resetting of x on each read which caused you to lose your line count. Additionally, your array is an indexed array, not an associative array.
Making the small adjustments and declareing the variables at the beginning (to hint to bash they are integers or arrays), it works as planned. note: there is no need for a here-string to assign the array, just use regular array syntax. Also note, for indexed arrays, there is no need to dereference the variable within [ ], simply ${array[i]} is fine.
While the use of expr is fine (old, slow, but portable and fine), you could use bash arithmetic syntax for your parameter calculations at the end. (you already have arrays). Finally, you are better served initializing each column[$i]=0 on the first read loop. You can do that with a simple flag:
#!/bin/bash
declare -i ncols=0
declare -i x=0
declare -a column
while read -r line; do
array=( $line )
[ $ncols -eq 0 ] && ncols=${#array[#]}
for ((i = 0; i < ncols; i++))
do
[ $x -eq 0 ] && column[$i]=0
column[$i]=$(( ${column[i]} + ${array[i]}))
done
((x++))
done < "$1"
for sum in ${column[#]}
do
average=`expr $sum / $x`
remainder=`expr $sum % $x`
mult=`expr $remainder \* 10`
fracvalue=`expr $mult / $x`
echo $x
echo $average
echo $sum
echo $remainder
echo $mult
echo $fracvalue
done
exit 0
Output
$ bash colavg.sh dat/colavg.txt
5
4
21
1
10
2
5
5
26
1
10
2
5
5
28
3
30
6
5
6
30
0
0
0
5
5
25
0
0
0