Column Read and Average (BASH) - bash

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

Related

How to print odd numbers in bash?

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

Bash syntax errors like `syntax error: operand expected (error token is "{1 % 2 ")`

Please tell why printing odd numbers in bash script with the following code gives the error:
line 3: {1 % 2 : syntax error: operand expected (error token is "{1 % 2 ")
for i in {1 to 99}
do
rem=$(( $i % 2 ))
if [$rem -neq 0];
then
echo $i
fi
done
This is working example:
for i in {1..99}
do
rem=$(($i % 2))
if [ "$rem" -ne "0" ]; then
echo $i
fi
done
used for loop have a typo in minimum and maximum number, should be {1..99} instead of {1 to 99}
brackets of the if statement needs to be separated with whitespace character on the left and on the right side
Comparision is done with ne instead of neq, see this reference.
As already pointed out, you can use this shell checker if you need some clarification of the error you get.
Not really sure why nobody included it, but this works for me and is simpler than the other 'for' solutions:
for (( i = 1; i < 100; i=i+2 )); do echo $i ; done
To print odd numbers between 1 to 99
seq 1 99 | sed -n 'p;n'
With GNU seq, credit to gniourf-gniourf
seq 1 2 99
Example
$ seq 1 10 | sed -n 'p;n'
1
3
5
7
9
if you reverse it will print even
$ seq 1 10 | sed -n 'n;p'
2
4
6
8
10
One liner:
for odd in {1..99..2}; do echo "${odd}"; done
Or print in a cluster.
for odd in {1..99..2}; do echo -n " ${odd} "; done
Likewise, to print even numbers only:
for even in {2..100..2}; do echo "${even}"; done
OR
for even in {2..100..2}; do echo -n " ${even} "; done
Replace {1 to 99} by {1..99}.
for (( i=1; i<=100; i++ ))
do
((b = $i % 2))
if [ $b -ne 0 ]
then
echo $i
fi
done
for i in {1..99}
do
rem=`expr $i % 2`
if [ $rem == 1 ]
then
echo "$i"
fi
done
for i in {0..49}
do
echo $(($i*2+1))
done

How to sequence through sets of numbers in shell/bash

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.

while looking for matching line 27: syntax error: unexpected end of file

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

put rows of numbers into a column with shell script

I have a file with many lines, like
1 jfkdajfd 1 2 3 5
2 fkldfjld
3 fdkfloaf 9 10
4 fldfldkf
5 fdskf;ak 12 1 4
I want to get all the numbers and put them in a column in a file, like
1
2
3
5
9
10
12
1
4
how can I achieve this?
thanks
In your case, it looks like you can do this:
awk '{for (i=3;i<=NF;++i) {print $i}}'
This is assuming that all the numbers you want to print occur in column 3 or after.
cat file | while read line
do
for i in $(echo ${line})
do
isnumeric=$(echo ${i} | grep -q [0-9]; echo ${?})
if [ ${isnumeric} -eq 0 ]
then
echo ${i} >> outfile
fi
done
done
not bulletproof and not as elegant as the previously given solutions, but it shows what is being used for determining if this is a numeric or not.
while read num alpha rest; do
[[ "$rest" ]] && printf "%s\n" $rest # <-- variable is unquoted
done < filename

Resources