Bash Script- Issue with variables and | bc - bash

So I have a variable that I want to compare with another number in an if statement.
b=8.25
if [ $(echo "$b < 10" | bc) -ne 0 ]; then
echo "hey"
fi
I get the following error
(standard_in) 1: syntax error
I know the issue is having the b variable inside, how can I make it so that I can maintain it in there?
Please help

Your script file probably has DOS-style CRLF line endings:
$ b=8.25
$ if [ $(echo "$b < 10" | bc) -ne 0 ]; then
> echo "hey"
> fi
hey
$ b=$'8.25\r'
$ if [ $(echo "$b < 10" | bc) -ne 0 ]; then
> echo "hey"
> fi
(standard_in) 1: illegal character: ^M
bash: [: -ne: unary operator expected
Run dos2unix on your script file.

Store the comparison in a variable separateley
b=8.25
# Capture output outside the if
comparison=$(echo "$b < 10" | bc)
# Use the output in the if
if [ $comparison -ne 0 ]; then
echo "hey"
fi

Related

bash comparing variables that are integers with -gt -lt

I'm trying to make a bash script that reads integers from a file (one number per line, name of the file is passed as the script argument), finds maximum, minimum and sum. I've got a problem with the part, where I'm comparing variables, though. Code below (I've skipped here the part which checks whether the file exists or is empty):
#!/bin/bash
min=`cat "$1" | head -n 1`
max=$min
sum=0
lw=`cat "$1" | wc -l`
while [ $lw -gt 0 ];
do
num=`cat "$1" | tail -n $lw | head -n 1`
if [ "$num" -gt "$max" ]
then
max=$num
elif [ "$num" -lt "$min" ]
then
min=$num
fi
sum=$[sum+num]
lw=$[$lw-1]
done
echo "Maximum: $max"
echo "Minimum: $min"
echo "Sum: $sum"
With this code I'm getting errors in lines 13 and 16: [: : integer expression expected
If I change the comparision part inside the while loop to:
if [ $num -gt $max ]
then
max=$num
elif [ $num -lt $min ]
then
min=$num
fi
I'm getting errors:
line 13: [: -gt: unary operator expected
line 16: [: -lt: unary operator expected
What am I doing wrong? I'm a total newbie in bash, so I'll be extremely grateful for any help.
Data that I used for testing:
5
6
8
2
3
5
9
10
Probably your input file contains DOS line endings or other improper formatting. Your code should work for well-formed inputs.
However, the proper way to loop over the lines in a file is
#!/bin/bash
min=$(sed 1q "$1")
max=$min
sum=0
while read -r num; do
if [ "$num" -gt "$max" ]
then
max=$num
elif [ "$num" -lt "$min" ]
then
min=$num
fi
((sum+=num))
done<"$1"
echo "Maximum: $max"
echo "Minimum: $min"
echo "Sum: $sum"
Notice also that backticks and $[[...]]] use syntax which has been obselescent for decades already.
My guess would be that the expression
num=`cat "$1" | tail -n $lw | head -n 1`
assigns to num some value that is not a number in one of the iterations. I would suggest adding echo "$num" in the prev line to check this assumption
Another thing: instead of reading lines using cat | tail | head it is easier to read file line by line using the following syntax
while IFS= read -r line
do
echo "$line"
done < "$input"
This will read contents of input file into line variable.
See here for explanations about IFS= and -r https://www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/ - both of them not really necessary in your case

Unary operator expected in shell script

I am not sure why my code below generates this error (standard_in) 1: syntax error -bash: [: -eq: unary operator expected . Can someone please help me figure out the problem here? Thanks!
#!/bin/bash
BAMLINES=4.47264e+09
FQ1LINES=4000000
FQ2LINES=4000000
DEBUG=1
if [ ! -z ${DEBUG} ]; then
echo "${BAMLINES} lines in .bam"
echo "${FQ1LINES} lines in all ${FQ_OUT1} files"
echo "${FQ2LINES} lines in all ${FQ_OUT2} files"
if [ $(echo "scale=2;${FQ1LINES}/${BAMLINES} > 0.40" | bc) -eq 0 ]; then
echo "Warning, FQ1 file contains ${FQ1LINES} lines - less than 40% of the number of reads of .bam file"
fi
if [ $(echo "scale=2;${FQ2LINES}/${BAMLINES} > 0.4" | bc) -eq 0 ]; then
echo "Warning, FQ2 file contains ${FQ2LINES} lines - less than 40% of the number of reads of .bam file"
fi
fi
$ echo "scale=2;($FQ1LINES/$BAMLINES) > 0.40"
scale=2;(4000000/) > 0.40
# ..............^^
You want to use either BAMLINE or BAMLINES but not both.
$ echo "scale=2;($FQ1LINES/$BAMLINES) > 0.40" | bc
(standard_in) 1: parse error
Because of that error, the output of $(echo ... | bc) is empty, and then [ only gets 2 arguments. When [ gets 2 arguments, the first operator is expected to be a unary operator (like -z is) -- -eq is not a unary operator.
You need to quote any variables/command expansions within [...]. In this case you'd get a different but more meaningful error:
$ [ "$(echo "scale=2;${FQ1LINES}/${BAMLINES} > 0.40" | bc)" -eq 0 ]
(standard_in) 1: parse error
bash: [: : integer expression expected
Or use [[...]] and you'll just see the bc error
$ [[ $(echo "scale=2;${FQ1LINES}/${BAMLINES} > 0.40" | bc) -eq 0 ]]
(standard_in) 1: parse error

Use of ` <` in bash script gives (standard_in) 1: syntax error

I'm getting this error as output
(standard_in) 1: syntax error
something something
something something
when i want is
something something
something something
Below is the bash script. After much trial and error checks i found the error is because of this line
done < /home/afsara/Desktop/ns2_offline/ns_code/wired.out;
What am I possibly doing wrong here?
#!/bin/bash
cd /
cd /home/afsara/Desktop/ns2_offline/ns_code/
#INPUT: output file AND number of iterations
output_file_format="tcp";
iteration_float=2.0;
end=5
iteration=$(printf %.0f $iteration_float);
r=5
while [ $r -le $end ]
do
###############################START A ROUND
l=1;thr=0.0;val=0.0
i=0
while [ $i -lt $iteration ]
do
while read val
do
dir="/home/afsara/Desktop/ns2_offline/ns_code/"
#dir=""
under="_"
all="all"
output_file="$dir$output_file_format$under$r$under$r$under$all.out"
echo -ne "Throughput: $thr " > $output_file
if [ $l == '1' ]; then
thr=$(echo "scale=5; $thr+$val/$iteration_float" | bc )
echo -ne "throughput: $val " >> $output_file
fi
echo "$val"
done < /home/afsara/Desktop/ns2_offline/ns_code/wired.out; #problem because of this
i=$(($i+1))
l=0
#################END AN ITERATION
done
r=$(($r+1))
#######################################END A ROUND
done
That's a bc error message, not a bash error message. It has nothing whatsoever to do with the done < /home/afsara/Desktop/ns2_offline/ns_code/wired.out, except perhaps that if you remove the redirection the loop has no input so its contents don't run at all.

Float conditional in bash

in bash I need to compare two float numbers, one which I define in the script and the other read as paramter, for that I do:
if [[ $aff -gt 0 ]]
then
a=b
echo "xxx "$aff
#echo $CX $CY $CZ $aff
fi
but I get the error:
[[: -309.585300: syntax error: invalid arithmetic operator (error token is ".585300")
What is wrong?
Thanks
Using bc instead of awk:
float1='0.43255'
float2='0.801222'
if [[ $(echo "if (${float1} > ${float2}) 1 else 0" | bc) -eq 1 ]]; then
echo "${float1} > ${float2}"
else
echo "${float1} <= ${float2}"
fi
use awk
#!/bin/bash
num1=0.3
num2=0.2
if [ -n "$num1" -a -n "$num2" ];then
result=$(awk -vn1="$num1" -vn2="$num2" 'BEGIN{print (n1>n2)?1:0 }')
echo $result
if [ "$result" -eq 1 ];then
echo "$num1 greater than $num2"
fi
fi
Both test (which is usually linked to as [)and the bash-builtin equivalent only support integer numbers.
Use bc to check the math
a="1.21231"
b="2.22454"
c=$(echo "$a < $b" | bc)
if [ $c = '1' ]; then
echo 'a is smaller than b'
else
echo 'a is larger than b'
fi
I would use awk for that:
e=2.718281828459045
pi=3.141592653589793
if [ "yes" = "$(echo | awk "($e <= $pi) { print \"yes\"; }")" ]; then
echo "lessthanorequal"
else
echo "larger"
fi
The simplest solution is this:
f1=0.45
f2=0.33
if [[ $f1 > $f2 ]] ; then echo "f1 is greater then f2"; fi
which (on OSX) outputs:
f1 is greater then f2
Here's another example combining floating point and integer arithmetic (you need the great little perl script calc.pl that you can download from here):
dateDiff=1.9864
nObs=3
i=1
while [[ $dateDiff > 0 ]] && [ $i -le $nObs ]
do
echo "$dateDiff > 0"
dateDiff=`calc.pl $dateDiff-0.224`
i=$((i+1))
done
Which outputs
1.9864 > 0
1.7624 > 0
1.5384 > 0

Bash Scripting and bc

I'm trying to write a bash script and I needed to do some floating point math. Basically I want to do something like this:
NUM=$(echo "scale=25;$1/10" | bc)
if [ $? -ne 0 ]
then
echo bad
fi
The problem I'm running into is $? tends to hold the output from the echo program and not the bc call. Is there a way I save the output from the bc program into a variable?
EDIT:
Thanks for the quick replies. Here's another way of looking at the problem. Say I modified the script a little bit so it looks like this:
#!/bin/bash
NUM=$(echo "scale=25;$1/10" | bc)
if [ $? -ne 0 ]
then
echo bad
exit
fi
echo "$NUM"
When the user inputs a normal floating point value, it works fine:
bash script.sh 1.0
output:
.1000000000000000000000000
However, when the user enters an incorrect value, the script can't recover:
bash script.sh 1.0a
output:
(standard_in) 1: parse error
What I'm trying to do is get it to exit gracefully.
I don't see anything wrong. $NUM is supposed to hold your bc command results
see:
NUM=$(echo "scale=25;$1/10" | bc)
echo "\$? is $?"
echo "NUM is $NUM"
output
$ ./shell.sh 10
$? is 0
NUM is 1.0000000000000000000000000
another way is to use awk
NUM=$(awk -vinput="$1" 'BEGIN{printf "%.25f", input/10 }')
echo "\$? is $?"
echo "NUM is $NUM"
The other way, is to do the check of "$1" before you pass to bc. eg
shopt -s extglob
input="$1"
case "$input" in
+([0-9.]))
IFS="."; set -- $input
if [ $# -ne 2 ];then
echo "bad decimal"
else
NUM=$(echo "scale=25;$1/10" | bc )
echo "$NUM"
fi
esac
you don't have to check for $? from bc anymore
For GNU bc, an error similar to "(standard_in) 1: syntax error" will be output on stderr. You can capture this in your variable and check for it.
#!/bin/bash
NUM=$(echo "scale=25;$1/10" | bc 2>&1)
if [[ $NUM =~ error || $? -ne 0 ]]
then
echo bad
exit
fi
echo "$NUM"
Are you after the result of calculation from bc (which you store in NUM) or the status return from the system call?
As I said you have the result of calculation in $NUM:
#bctest.sh
NUM=$(echo "scale=25;$1/10" | bc)
if [ $? -ne 0 ]
then
echo bad
fi
echo "result: ", $NUM
Test:
bash ./bctest.sh 15
result: , 1.5000000000000000000000000

Resources