Compare a float with a defined number in shell - shell

I have the following script, which I want to use to compare a float to another given float.
#!/bin/sh
echo 'Enter a real number'
read n
echo n=$n
if (( $(echo "$n > 0.0" |bc -l) ))
then
echo 'n is +ve'
elif (( $(echo "$n < 0.0" |bc -l) ))
then
echo 'n is -ve'
else
echo 'n is zero'
fi
This works fine in my OS X, however the if statement shows error ("not found") in Linux (Ubuntu).
What could be the more universal syntax ? (I guess one can still work with bc for floats).

I find it convenient to use awk:
if echo $n | awk '$0 > 0.0' | grep -q . ; then
echo 'n is +ve'
elif echo $n | awk '$0 < 0.0' | grep -q . ; then
echo 'n is -ve'
else
echo 'n is zero'
fi

Related

getops if than else

I have working getops function
m) MONTH=$OPTARG
for file in *eqllogmgr* ;
do ftx $file |
layoutcc ;
cat $file | grep '\-'$MONTH |
perl -ne 'print if /-2018/' |
perl -pe 's/......-2018/\e[1;32m$&\e[0m/g' |
sed '$d' ;
echo ;
ft1 ;
done ;;
I am trying to alter the grep to evaluate the $MONTH and use the grep i need depending on what the user inputs for the MONTH.
I tried this
t) MONTH=$OPTARG
if [[ $MONTH =~ ^[0-9]+$ ]]; then
for file in *eqllogmgr* ;
do ftx $file |
layoutcc ;
cat $file | grep $MONTH |
perl -ne 'print if /-2018/' |
perl -pe 's/......-2018/\e[1;32m$&\e[0m/g' |
sed '$d' ;
echo ;
ft1 ;
fi ;
done ;;
else
for file in *eqllogmgr* ;
do ftx $file |
layoutcc ;
cat $file | grep '\-'$MONTH |
perl -ne 'print if /-2018/' |
perl -pe 's/......-2018/\e[1;32m$&\e[0m/g' |
sed '$d' ;
echo ;
ft1 ;
fi ;
done ;;
But i get errors for the fi
i have tried moving that fi around and i cant seem to figure out what i am missing.
Basicly i want to look for a number in the variable and if it has one
run the grep on the base variable $MONTH
If it has no number i want to grep the variable with a dash in front of it
'-'$MONTH
I tired this as well and i get the same
syntax error near unexpected token `else'
so i am pretty sure its just the way i am using the if else statement
u) MONTH=$OPTARG
for file in *eqllogmgr* ;
do ftx $file |
layoutcc ;
cat $file |
if [[ $MONTH =~ ^[0-9]+$ ]]; then
grep $MONTH |
else
grep '\-'$MONTH |
fi
perl -ne 'print if /-2018/' |
perl -pe 's/......-2018/\e[1;32m$&\e[0m/g' |
sed '$d' ;
echo ;
ft1 ;
done ;;
Adding my final working version for reference ===========================
m) MONTH=$OPTARG
if [[ "$MONTH" = [0-9]* ]]; then
grep_expr=$MONTH
else
grep_expr='\-'$MONTH
fi
for file in *eqllogmgr* ; do
ftx "$file" | layoutcc
grep "$grep_expr" "$file" | perl -ne 'print if /-2018/' | perl -pe 's/......-2018/\e[1;32m$&\e[0m/g' | sed '$d'
echo
ft1
done
;;
What (I think) you're trying to do:
case-value)
if ... ; then
for ...; do
done
else
for ...; do
done
fi
;;
What you have shown in your question:
case-value)
if ...; then
for ...; do
fi
done
;;
else
for ...; do
fi
done
;;
The first fi and the first ;; are both extraneous. The second fi is misplaced inside the second for loop.
In the edit you made to your question, you seem to have changed tack and are now trying to accomplish your goal with a single for loop containing an if/else, as opposed to an if/else with a for loop in each clause. That's definitely more efficient, but you can't use an if/else the way you're trying to. At least, not without a lot of unnecessary messiness (actually you can, but you have to get rid of a couple extra |s -- see below). Since your grep expression only varies by a couple characters, you can just store the expression in a variable, and use your if/else to set the expression:
u) MONTH=$OPTARG
if [[ "$MONTH" =~ ^[0-9]+$ ]]; then
grep_expr='\-'$MONTH
else
grep_expr=$MONTH
fi
for file in *eqllogmgr* ; do
ftx "$file" | layoutcc
grep "$grep_expr" "$file" |
perl -ne 'print if /-2018/' |
perl -pe 's/......-2018/\e[1;32m$&\e[0m/g' |
sed '$d'
echo
ft1
done
;;
Note: I also added some quoting, removed the unnecessary colons, and got rid of the unnecessary cat command (just pass the filename directly to grep - no need for an extra pipe).
* Turns out what you were trying would actually work, although I personally don't think it's very concise. Example:
$ show=men
$ echo -e "Alice\nBob\nCarol\nDavid" | if [[ "$show" = "men" ]]; then egrep 'Bob|David'; else egrep 'Alice|Carol'; fi
Bob
David
$ show=women
$ echo -e "Alice\nBob\nCarol\nDavid" | if [[ "$show" = "men" ]]; then egrep 'Bob|David'; else egrep 'Alice|Carol'; fi
Alice
Carol

bash compare the numeric value of multiple columns

i have a variable which it output yileds 2 columns:
echo "$SIZE_TO_SOCKET"
A 256
B 256
My gloal is basically to compare the 2 numbers and print a massage
somthing like :if they match : Success: A(256) is euqal to B(256)
or Error: A(256) is not euqal to B(256) if there is the numbers are not euqal
My code is:
while IFS= read -r i;do
SIZE=$(echo "$i"|awk '{print $2}')
SOCKET=$(echo "$i"|awk '{print $1}')
if [[ "$SOCKET" = "A" ]] ; then
SOCKET_A="$SIZE"
elif [[ "$SOCKET" = "B" ]] ; then
SOCKET_B="$SIZE"
fi
if [[ $SOCKET_A -ne $SOCKET_B]];then
echo "error: SOCKET_A is not equal to SOCKET_B"
elif [[ $SOCKET_A -eq $SOCKET_B]];then
cho "success: SOCKET_A is equal to SOCKET_B"
fi
done <<< "$SIZE_TO_SOCKET"
the code doesn't yields any output ,anyway there is properly shorter and more elegant way
input="A 256\nB 256"
arr=($(echo -e "$input"))
if [[ "${arr[1]}" -eq "${arr[3]}" ]]
then
echo "success: ${arr[0]}(${arr[1]}) is equal to ${arr[2]}(${arr[3]})"
else
echo "error: ${arr[0]}(${arr[1]}) is not equal to ${arr[2]}(${arr[3]})"
fi
Replace the input variable by your input and this script outputs either
success: A(256) is equal to B(256)
for two same numbers or
error: A(257) is not equal to B(256)
for two different numbers.
This was pretty straightforward solution I ended up with:
line1=$(echo "$SIZE_TO_SOCKET" | awk 'NR == 1')
line2=$(echo "$SIZE_TO_SOCKET" | awk 'NR == 2')
key1=$(echo "$line1" | cut -d ' ' -f 1)
value1=$(echo "$line1" | cut -d ' ' -f 2)
key2=$(echo "$line2" | cut -d ' ' -f 1)
value2=$(echo "$line2" | cut -d ' ' -f 2)
if [[ $value1 -eq $value2 ]]; then
echo "Success: $key1($value1) is equal to $key2($value2)"
else
echo "Error: $key1($value1) is not equal to $key2($value2)"
fi
What is wrong with your code?:
your code "corrected" would be:
SIZE_TO_SOCKET=$(printf '41203235360a42203235360a' | xxd -p -r)
while IFS= read -r i;do
SIZE=$(echo "$i"|awk '{print $2}')
SOCKET=$(echo "$i"|awk '{print $1}')
if [[ "$SOCKET" = "A" ]] ; then
SOCKET_A="$SIZE"
elif [[ "$SOCKET" = "B" ]] ; then
SOCKET_B="$SIZE"
fi
if [[ $SOCKET_A -ne $SOCKET_B ]] ; then
echo "error: SOCKET_A is not equal to SOCKET_B"
elif [[ $SOCKET_A -eq $SOCKET_B ]] ; then
echo "success: SOCKET_A is equal to SOCKET_B"
fi
done <<< "$SIZE_TO_SOCKET"
you needed spaces in different places like this one $SOCKET_B ]] ; then (your original code is $SOCKET_B]];then)
the output of your "corrected" code is:
error: SOCKET_A is not equal to SOCKET_B
success: SOCKET_A is equal to SOCKET_B
if you use bash -x YOUR_SCRIPT, you get:
++ printf 41203235360a42203235360a
++ xxd -p -r
+ SIZE_TO_SOCKET='A 256
B 256'
+ IFS=
+ read -r i
++ echo 'A 256'
++ awk '{print $2}'
+ SIZE=256
++ echo 'A 256'
++ awk '{print $1}'
+ SOCKET=A
+ [[ A = \A ]]
+ SOCKET_A=256
+ [[ 256 -ne '' ]]
+ echo 'error: SOCKET_A is not equal to SOCKET_B'
error: SOCKET_A is not equal to SOCKET_B
+ IFS=
+ read -r i
++ echo 'B 256'
++ awk '{print $2}'
+ SIZE=256
++ echo 'B 256'
++ awk '{print $1}'
+ SOCKET=B
+ [[ B = \A ]]
+ [[ B = \B ]]
+ SOCKET_B=256
+ [[ 256 -ne 256 ]]
+ [[ 256 -eq 256 ]]
+ echo 'success: SOCKET_A is equal to SOCKET_B'
success: SOCKET_A is equal to SOCKET_B
+ IFS=
+ read -r i
read that carefully; you have an error in your logic.
this could be the corrected version of your code:
SIZE_TO_SOCKET=$(printf '41203235360a42203235360a' | xxd -p -r)
while IFS= read -r i;do
SIZE=$(echo "$i"|awk '{print $2}')
SOCKET=$(echo "$i"|awk '{print $1}')
if [[ "$SOCKET" = "A" ]] ; then
SOCKET_A="$SIZE"
elif [[ "$SOCKET" = "B" ]] ; then
SOCKET_B="$SIZE"
fi
done <<< "$SIZE_TO_SOCKET"
if [[ $SOCKET_A -ne $SOCKET_B ]] ; then
echo "error: SOCKET_A is not equal to SOCKET_B"
elif [[ $SOCKET_A -eq $SOCKET_B ]] ; then
echo "success: SOCKET_A is equal to SOCKET_B"
fi
the output is:
success: SOCKET_A is equal to SOCKET_B
and if you use bash -x YOUR_CORRECTED_SCRIPT, you get:
++ printf 41203235360a42203235360a
++ xxd -p -r
+ SIZE_TO_SOCKET='A 256
B 256'
+ IFS=
+ read -r i
++ echo 'A 256'
++ awk '{print $2}'
+ SIZE=256
++ echo 'A 256'
++ awk '{print $1}'
+ SOCKET=A
+ [[ A = \A ]]
+ SOCKET_A=256
+ IFS=
+ read -r i
++ echo 'B 256'
++ awk '{print $2}'
+ SIZE=256
++ echo 'B 256'
++ awk '{print $1}'
+ SOCKET=B
+ [[ B = \A ]]
+ [[ B = \B ]]
+ SOCKET_B=256
+ IFS=
+ read -r i
+ [[ 256 -ne 256 ]]
+ [[ 256 -eq 256 ]]
+ echo 'success: SOCKET_A is equal to SOCKET_B'
success: SOCKET_A is equal to SOCKET_B

Product of a digit with a shell script

How do I get the product of a 1 number in bash? (12345 > 1x2x3x4x5)
I am trying to get a script to do multiplication, I have tried escaping the * char but no luck, I have also tried fiddling with expr.
echo -n "Enter number "
read n
len=$(echo $n | wc -c)
len=$(( $len - 1 ))
for (( i=1; i <= $len; i++ ))
do
prod=$(($prod \* $(echo $n | cut -c $i) ))
done
echo "Product of $n is $prod"
You can get the length of a string from parameter expansion, no need to call external tools.
#!/bin/bash
read -p 'Enter number: ' n
product=1
for (( i=0; i < ${#n}; ++i )) ; do
d=${n:i:1}
(( product *= d ))
done
printf '%d\n' $product
And, for fun, using Perl:
perl -MList::Util=product -le 'print product(split //, shift)' -- "$n"
gawk (GNU awk) solution:
echo 12345 | awk -v FPAT="[0-9]" '{ r=$1; for(i=2;i<=NF;i++) r=r*$i }END{ print r }'
The output:
120
FPAT="[0-9]" - represents field value so that each field contains a digit
As I understood you want to get value of digits multiplication in number
Suppose you have number:
number=12345
You have to insert * between digits:
echo $number | sed 's/\([0-9]\)/\1*/g'
We will get string:
1*2*3*4*5*
We don't need last asteriks - let's remove it:
echo $number | sed 's/\([0-9]\)/\1*/g' | sed 's/.$//g'
We will get this:
1*2*3*4*5
We can now redirect it to calc:
echo $number | sed 's/\([0-9]\)/\1*/g' | sed 's/.$//g' | calc -p
It's stdout is:
120
\* is wrong in an arithmetic expression, it should be * alone. But even then running your code gives:
$ bash product.sh
Enter number 12
product.sh: line 10: * 1 : syntax error: operand expected (error token is "* 1 ")
Product of 12 is
The reason for the error is that $prod variable is not set to an
initial value before so it's expanded to an empty value, for example
try it in your terminal:
$ echo $prod
$
In your script you should set prod to an initial value before using
it for the first time. It should be:
echo -n "Enter number "
read n
len=$(echo $n | wc -c)
len=$(( $len - 1 ))
prod=1
for (( i=1; i <= $len; i++ ))
do
prod=$(($prod * $(echo $n | cut -c $i) ))
done
echo "Product of $n is $prod"
There are a few more problems with your code:
always put a shebang line at the top
always double quote the variables
using $ on variables is not necessary in arithmetic expressions in Bash

Grep command in array

For a homework assignment I have to Take the results from the grep command, and write out up to the first 5 of them, numbering them from 1 to 5. (Print the number, then a space, then the line from grep.) If there are no lines, print a message saying so. So far I managed to store the grep command in an array but this is where I've gotten stuck: Can anyone provide guidance as to how to proceed in printing this as stated above
pattern="*.c"
fileList=$(grep -l "main" $pattern)
IFS=$"\n"
declare -a array
array=$fileList
for x in "${array[#]}"; do
echo "$x"
done
you can grep options -c and -l
pattern="*.c"
searchPattern="main"
counter=1
while read -r line ; do
IFS=':' read -r -a lineInfo <<< "$line"
if [[ $counter > 5 ]]; then
exit 1
fi
if [[ ${lineInfo[1]} > 0 ]]; then
numsOfLine=""
while read -r fileline ; do
IFS=':' read -r -a fileLineInfo <<< "$fileline"
numsOfLine="$numsOfLine ${fileLineInfo[0]} "
done < <(grep -n $searchPattern ${lineInfo[0]})
echo "$counter ${lineInfo[0]} match on lines: $numsOfLine"
let "counter += 1"
else
echo "${lineInfo[0]} no match lines"
fi
done < <(grep -c $searchPattern $pattern)
If you're only allowed to use grep and bash(?):
pattern="*.c"
fileList=($(grep -l "main" $pattern))
if test ${#fileList[#]} = 0 ; then
echo "No results"
else
n=0
while test $n -lt ${#fileList[#]} -a $n -lt 5 ; do
i=$n
n=$(( n + 1 ))
echo "$n ${fileList[$i]}"
done
fi
If you are allowed to use commands in addition to grep, you can pipe the results through nl to add line numbers, then head to limit the results to the first 5 lines, then a second grep to test if there were any lines. For example:
if ! grep -l "main" $pattern | \
nl -s ' ' | sed -e 's/^ *//' | \
head -n 5 | grep '' ; then
echo "No results"
fi

sed command to select certain number of lines from a file

I am trying to split huge files each of which will contain around say 30k of lines.
I found it can be done using sed -n 'from_line,to_line p' command but it is working fine if i have line numbers but here in my case i am using two variable and i am getting error for that.
here is script which i am using.
k=1
for i in `ls final*`
do
count=`wc -l $i|awk '{print $1}'`
marker1=1
marker2=30000
no_of_files=$(( count/30000 ))
#echo $no_of_files
no_of_files=$(( no_of_files+1 ))
while [[ no_of_files -ne 0 ]];do
if [[ $marker2 -gt $count ]];then
sed -n '$marker1,$count p' $i > purge$k.txt
else
sed -n '$marker1,$marker2 p' $i > purge$k.txt
marker1=$(( marker2+1 ))
marker2=$(( marker2+30000 ))
fi
no_of_files=$(( no_of_files-1 ))
k=$(( k+1 ))
done
done
I am getting below error while running the script.
sed: $marker1,$marker2 p is not a recognized function.
sed: $marker1,$marker2 p is not a recognized function.
sed: $marker1,$marker2 p is not a recognized function.
sed: $marker1,$marker2 p is not a recognized function.
sed: $marker1,$marker2 p is not a recognized function.
sed: $marker1,$marker2 p is not a recognized function.
sed: $marker1,$count p is not a recognized function.
It doesnt work probably because you use variables in ''
try to change sed commands as follow
sed -n "$marker1,$count p"
or better is this
sed -n '/'$marker1'/,/'$count'/p'
Some small changes.
Use double quote in sed. Do not use old back tics, use parentheses.
Change k=$(( k+1 )) to ((k++)).
k=1
for i in $(ls final*)
do
count=$(wc -l <$i)
marker1=1
marker2=30000
no_of_files=$(( count/30000 ))
#echo $no_of_files
(( no_of_files++ ))
while [[ no_of_files -ne 0 ]];do
if [[ $marker2 -gt $count ]];then
sed -n "$marker1,$count p" $i > purge$k.txt
else
sed -n "$marker1,$marker2 p" $i > purge$k.txt
marker1=$(( marker2+1 ))
marker2=$(( marker2+30000 ))
fi
(( no_of_files-- ))
(( k++ ))
done
done
This wc -l $i|awk '{print $1}' could be used like this:
awk 'END {print NR}' $i
or
wc -l < $i
As others have noted, you have your shell variables inside single quotes so they are not being expanded. But you are using the wrong tool. What you are doing creates N files using N passes. split -l 30000 "$i" will split the file into 30,000 line pieces called xaa, xab, ... You can tell split what to call the xaa files too.

Resources