Bourne shell: reading multiple lines of stdin - shell

I am new to Bourne shells and was curious about reading stdin on multiple lines/that includes newline.
IN: For example, if someone types in
1
3 #only ignore whitespace
5.3 #ignore floating point number all together
I could have it read them and echo them on separate lines. My goal is to also ignore floating point numbers (integers only!) and ignore whitespace.
OUT: My ideal output would calculate the average (example with above input)
1
2
So far I can read stdin and echo it back if it is on one line and limited to a hardcoded value (should be whenever user stops with Ctrl-D).
#! /bin/sh
read a b c
echo "$a";
echo "$b";
echo "$c";

It is been a while, but the following should obtain a sum and average for only integer input:
#!/bin/sh
sum=0;
cnt=0
while read line
do
case "$line" in
*[.]* )
continue
;;
[0-9]* )
sum=`expr "$sum" + "$line"`
cnt=`expr "$cnt" + 1`
;;
esac
done
printf "\nThe sum is '%s', cnt is '%s' the average is '%s'\n\n", "$sum" "$cnt" `expr $sum / $cnt`
Output
$ printf "1\n 3\n5.3\n" | sh bourne_sum.sh
The sum is '4', cnt is '2' the average is '2'
Input in a File
$ cat dat/bsum.txt
1
3
5.3
Simple redirection will do:
$ sh bourne_sum.sh <dat/bsum.txt
The sum is '4', cnt is '2' the average is '2'
Modified to output on 1 line
#!/bin/sh
sum=0;
cnt=0
while read line
do
case "$line" in
*[.]* )
printf " 0"
continue
;;
[0-9]* )
sum=`expr "$sum" + "$line"`
cnt=`expr "$cnt" + 1`
printf " %s" `expr $sum / $cnt`
;;
esac
done
printf "\n"
# printf "\nThe sum is '%s', cnt is '%s' the average is '%s'\n\n" "$sum" "$cnt" `expr $sum / $cnt`
Output
$ printf "1\n3\n5\n4.4\n" | sh bourne_sum.sh
1 2 3 0
Responding to ONLY Integers
#!/bin/sh
sum=0;
cnt=0
while read line
do
if [ $line -eq $line 2>/dev/null ]
then
sum=`expr "$sum" + "$line"`
cnt=`expr "$cnt" + 1`
printf " %s" `expr $sum / $cnt`
fi
done
printf "\n"
Output
$ printf "1\n3\n5\n4.4\n'2 3'\n" | sh bourne_sum.sh
1 2 3

Related

Error getting in the second if statement in bash script

n=20
x=3
count=0
flag=0
i=1
declare -a arr[n+1]
for (( j=0;j<=n;j++ ))
do
arr+=(0)
done
#echo "${arr[#]}"
while [[ $count -ne $n ]]
do
if [[ $i -le $n ]]
then
if [[ ${arr[$i]} -eq '0' ]]
then
echo "Value is ${arr[$i]}"
#${arr[$(i-1)]}= (( ${arr[$i-1]++} ))
${arr[$i]}+=${arr[$i]}
echo " "
#echo -n "${arr[$i]}"
echo -n " $i"
count=$(( count+1 ))
i=$(( i+1+x ))
else
i=$(( i+1 ))
fi
else
i=$(( i-n ))
flag=$(( flag+1 ))
fi
done
echo " "
echo "No of round : $flag"
This is the whole code, I've tried to print numbers that follows this: n=20 is the number of elements and x=3 is the number that we have to avoid. For example,
20
3
1,5,9,13,17,2,6,10,14,18,3,7,11,15,19,4,8,12,16,20,
3
But, the problem is that my second if condition is not fulfilling, if ignores the condition. Above example is for the C++, but in bash script, 2nd if statement isn't working. This can be because syntax is wrong. So can you please help me to find the mistakes.
Output of the above code:
output
${arr[$i]}+=${arr[$i]}
This is incorrect. $ should not be used when you assign the value.
If you want to double the value, replace this string with the following:
arr[$i]=$(( ${arr[$i]} + ${arr[$i]} ))
Or what you want to do there?

Issue with program check if the number is divisible by 2 with no remainder BASH

I tried to write a program to see if the count divisible by 2 without a remainder
Here is my program
count=$((count+0))
while read line; do
if [ $count%2==0 ]; then
printf "%x\n" "$line" >> file2.txt
else
printf "%x\n" "$line" >> file1.txt
fi
count=$((count+1))
done < merge.bmp
This program doesnt work its every time enter to the true
In the shell, the [ command does different things depending on how many arguments you give it. See https://www.gnu.org/software/bash/manual/bashref.html#index-test
With this:
[ $count%2==0 ]
you give [ a single argument (not counting the trailing ]), and in that case, if the argument is not empty then the exit status is success (i.e. "true"). This is equivalent to [ -n "${count}%2==0" ]
You want
if [ "$(( $count % 2 ))" -eq 0 ]; then
or, if you're using bash
if (( count % 2 == 0 )); then
Some more "exotic" way to do this:
count=0
files=(file1 file2 file3)
num=${#files[#]}
while IFS= read -r line; do
printf '%s\n' "$line" >> "${files[count++ % num]}"
done < input_file
This will put 1st line to file1, 2nd line to file2, 3rd line to file3, 4th line to file1 and so on.
awk to the rescue!
what you're trying to do is a one-liner
$ seq 10 | awk '{print > (NR%2?"file1":"file2")}'
==> file1 <==
1
3
5
7
9
==> file2 <==
2
4
6
8
10
try
count=$((count+0))
while read line; do
if [ $(($count % 2)) == 0 ]; then
printf "%x\n" "$line" >> file2.txt
else
printf "%x\n" "$line" >> file1.txt
fi
count=$((count+1))
done < merge.bmp
You have to use the $(( )) around a mod operator as well.
How to use mod operator in bash?
This will print "even number":
count=2;
if [ $(($count % 2)) == 0 ]; then
printf "even number";
else
printf "odd number";
fi
This will print "odd number":
count=3;
if [ $(($count % 2)) == 0 ]; then
printf "even number";
else
printf "odd number";
fi

number palindrome in shell scripting

#!/bin/sh
echo “Enter number:”
read n
num=$n
rev=0
while [ $num –ne 0 ]
do
rem= `expr $num % 10`
rev= `expr $rev \* 10 + $rem`
num= `expr $num / 10`
done
if [ $rev –eq $n ]
then
echo $n “is a palindrome”
else
echo $n “is not a palindrome”
fi
I am getting errors as::
unary operator expected
Please help me in fixing this.
What an unexpected problem! Your dash - character is not the right dash.
The hex representation of your dash – is 0xe28093
Unicode Character 'EN DASH' (U+2013).
But the right dash's hex representation is ASCII 0x2d.
echo -n '–' | xxd # 00000000: e280 93
echo -n '-' | xxd # 00000000: 2d
So you should change it to the correct dash.
#!/bin/bash
echo -n "Enter number : "
read n
# store single digit
sd=0
# store number in reverse order
rev=""
# store original number
on=$n
while [ $n -gt 0 ]
do
sd=$(( $n % 10 )) # get Remainder
n=$(( $n / 10 )) # get next digit
# store previous number and current digit in reverse
rev=$( echo ${rev}${sd} )
done
if [ $on -eq $rev ];
then
echo "Number is palindrome"
else
echo "Number is NOT palindrome"
fi
Source : https://bash.cyberciti.biz/academic/palindrome-shell-script/

How to redirect variable into text file then sort alphabetical

My script is to ask for input 1 upper case at a time and end with 0 invalid input will need to be displayed and display the first valid upper letter.
#! /bin/sh
count=0
until [[ $n =~ 0 ]]; do
echo Inputs:
read n
if [[ $n =~ ^[A-Z]$ ]]; then
count=`expr $count + 1`
echo $n | sort > out.txt
fi
done
echo The total number of valid input letters:
echo $count
echo " "
echo The first valid input:
head -n 1 /filepath/out.txt
Output:
Inputs:
B
Inputs:
A
Inputs:
C
Inputs:
0
The total number of valid input letters:
3
The first valid input:
C
Question: It should result in A.
Any help will be appreciated.
This line:
echo $n | sort > out.txt
always zaps the file out.txt with just the latest input. Maybe you should use:
cp /dev/null out.txt # Before the loop
echo $n | sort -o out.txt out.txt -
The cp command creates an empty file. The sort command reads the existing file out.txt and its standard input (the new line), sorts the result and writes it out over out.txt.
This is OK for short inputs; it isn't very efficient if it needs to scale to thousands of lines.
Also, in Bash, you don't need to use expr for arithmetic:
((count++))
Use the following code.
#! /bin/sh
count=0
>out.txt
until [[ $n =~ 0 ]]; do
read -p 'Inputs: ' n
if [[ $n =~ ^[A-Z]$ ]]; then
count=`expr $count + 1`
echo $n >> out.txt
fi
done
echo The total number of valid input letters:
echo $count
echo " "
echo The first valid input:
sort out.txt |head -n 1
Output:
Inputs: B
Inputs: A
Inputs: C
Inputs: 0
The total number of valid input letters:
3
The first valid input:
A
Since you want only the smallest (in alphabet order) valid input, you don't need sort. Here's an alternative answer not using sort but just keep the smallest valid input:
#!/bin/sh
count=0
until [[ $n =~ 0 ]]; do
echo Inputs:
read n
if [[ $n =~ ^[A-Z]$ ]]; then
((count++))
if [ -z $first ] || [ `expr $n \< $first` -eq 1 ]; then
first=$n
fi
fi
done
echo The total number of valid input letters:
echo $count
echo " "
echo The first valid input:
echo $first

how to extract numbers from this echo into separate variables?

Sorry about bits and snippit of information
So I am writing an average shell script program
so if use inputs
echo 1 3, .... | sh get_number
I would have to pull the numbers seperated by spaces from echo to be
var1 = 1, var2= 3, etc.
I tried
#!/bin/sh
sum=0
for i in $*
do
sum=`expr $sum + $i`
done
avg=`expr $sum / $n`
echo Average=$avg
but doesnt work....
do I include a read here?
also how would I do
sh get_number <file1>, <file2>... to grab numbers in them and sum them
in shell script?
Thanks
Sounds like you are looking for the read shell builtin:
% echo "1 2 3 4" | read a b stuff
% echo $b
2
% echo $stuff
3 4
To fix up your code:
for i in $*; do
sum=$(( sum + i ))
n=$(( n + 1 ))
done
echo "Average=$(( sum / n ))"
#!/bin/sh
while [ $# -gt 0 ]; do
(( i++ ))
(( sum += $1 ))
shift
done
echo "Average=$(( sum/i ))"
Note: This fails in dash which is the closest shell I could find to a real sh.
An example of reading values from files passed as command line arguments or from lines read from stdin:
add_to_sum() {
set $*
while [ $# -gt 0 ]; do
I=`expr $I + 1`
SUM=`expr $SUM + $1`
shift
done
}
I=0
SUM=0
if [ $# -gt 0 ]; then
# process any arguments on the command line
while [ $# -gt 0 ]; do
FILE=$1
shift
while read LINE; do
add_to_sum "$LINE"
done < "$FILE"
done
else
# if no arguments on the command line, read from stdin
while read LINE; do
add_to_sum "$LINE"
done
fi
# be sure not to divide by zero
[ $I -gt 0 ] && echo Average=`expr $SUM / $I`

Resources