How to find min of two variables in linux - linux

i have two variables
$a and $b
is there shell script command to assign the value of minimum variable to $c
i.e
$c = min($a,$b)
and some command that can work across all platforms hp-ux,aix,linux_x_64
thanks in advance
EDIT:
default shell is ksh
and below the script im trying to run
rm abc.log
near_dr=`sqlplus -s tcs384160/tcs#1234 <<\EOF
set pagesize 0 feedback off verify off heading off echo off
select max(sequence#) from v$archived_log where applied='YES' and thread#=1 and
dest_id=2;
exit;
EOF`
DR=`sqlplus -s tcs384160/tcs#1234 <<\EOF
set pagesize 0 feedback off verify off heading off echo off
select max(sequence#) from v$archived_log where applied='YES' and thread#=1 and
dest_id=3;
exit;
EOF`
safe_var=$([ $near_dr -le $DR ] && echo "$near_dr" || echo "$DR")
echo $safe_var;
ulimit=`expr $safe_var - 30`;
llimit=`expr $ulimit - 1000`;
echo $llimit;
echo $ulimit;
i=$llimit
while [ $i -le $ulimit ];
do
ls evdprd_1_${i}_*.arc>>abc.log;
let i=i+1;
done;
recover -s ttlhydnwr -c tphtpsd2<<EOF >> abc.log
ls -1 *.arc
exit
EOF
sed -e 's/[\t ]//g;/^$/d' abc.log > abc1.log
awk '++seen[$0] == 2' abc1.log > actual.log
please ignore the sqlplus parts as those are working fine the only problem is the $safe_var needing the minimum of the two values

There is no function, but you can create it:
(( $a <= $b )) && echo "$a" || echo "$b"
The condition && action1 || action2 does evaluate the condition. If it is true, then perform action1; otherwise, perform action2.
To store the result into a variable, do:
min=$( (( $a <= $b )) && echo "$a" || echo "$b" )
Update
It seems that the $( (( )) ) syntax is giving problems. Hence, let's replace it to:
[ $a -le $b ] && echo "$a" || echo "$b"
or assigning value:
min=$([ $a -le $b ] && echo "$a" || echo "$b")
Sample
$ a=3
$ b=4
$ [ $a -le $b ] && echo "$a" || echo "$b"
3
$ b=1
$ [ $a -le $b ] && echo "$a" || echo "$b"
1
$ b=3
$ [ $a -le $b ] && echo "$a" || echo "$b"
3

You could do in this way too:
x=1
y=2
echo $(($x<$y?$x:$y))
1
To store value in other variable:
z=$(($x<$y?$x:$y)) #Min
z=$(($x<=$y?$x:$y)) #Min and equal
z=$(($x>$y?$x:$y)) #Greater
z=$(($x>=$y?$x:$y)) #Greater and equal

In ksh, this works fine
$ near_dr=5 DR=10
$ save_var=$(( near_dr < DR ? near_dr : DR )) ; echo $save_var
5
$ near_dr=15 DR=10
$ save_var=$(( near_dr < DR ? near_dr : DR )) ; echo $save_var
10
$ near_dr=15 DR=15
$ save_var=$(( near_dr < DR ? near_dr : DR )) ; echo $save_var
15
So you can write a function:
min() {
echo $(( $1 < $2 ? $1 : $2 ))
}
safe_var=$(min $near_dr $DR)
This is the same as #Liaraz's answer, except "Variables can be referenced by name within an arithmetic
expression without using the parameter expansion syntax." (ksh man page, section Arithmetic evaluation)

Ksh93 custom arithmetic function example:
function .sh.math.min a b {
.sh.value=$(( a<b ? a : b ))
}
$ echo $(( min(1,3) ))
1
$ echo $(( min(5,3) ))
3

if you are sure the value of variables are integers, use #fedorqui's answer. It's short and neat.
however, if you are not sure about that, they could be e.g. float numbers:
kent$ a=3.57
kent$ b=13.5
kent$ (($a < $b)) && echo "y" || echo "n"
bash: ((: 3.57 < 13.5: syntax error: invalid arithmetic operator (error token is ".57 < 13.5")
n
this would work for all cases:
kent$ awk -v a="$a" -v b="$b" 'BEGIN{print (a>b)?b:a}'
3.57

Related

Bash script outputs wrong answer [duplicate]

I'm unable to get numeric comparisons working:
echo "enter two numbers";
read a b;
echo "a=$a";
echo "b=$b";
if [ $a \> $b ];
then
echo "a is greater than b";
else
echo "b is greater than a";
fi;
The problem is that it compares the number from the first digit on, i.e., 9 is bigger than 10, but 1 is greater than 09.
How can I convert the numbers into a type to do a true comparison?
In Bash, you should do your check in an arithmetic context:
if (( a > b )); then
...
fi
For POSIX shells that don't support (()), you can use -lt and -gt.
if [ "$a" -gt "$b" ]; then
...
fi
You can get a full list of comparison operators with help test or man test.
Like this:
#!/bin/bash
a=2462620
b=2462620
if [ "$a" -eq "$b" ]; then
echo "They're equal";
fi
Integers can be compared with these operators:
-eq # Equal
-ne # Not equal
-lt # Less than
-le # Less than or equal
-gt # Greater than
-ge # Greater than or equal
See this cheatsheet.
There is also one nice thing some people might not know about:
echo $(( a < b ? a : b ))
This code will print the smallest number out of a and b
In Bash I prefer doing this as it addresses itself more as a conditional operation unlike using (( )) which is more of arithmetic.
[[ n -gt m ]]
Unless I do complex stuff like
(( (n + 1) > m ))
But everyone just has their own preferences. Sad thing is that some people impose their unofficial standards.
You can also do this:
[[ 'n + 1' -gt m ]]
Which allows you to add something else which you could do with [[ ]] besides arithmetic stuff.
The bracket stuff (e.g., [[ $a -gt $b ]] or (( $a > $b )) ) isn't enough if you want to use float numbers as well; it would report a syntax error. If you want to compare float numbers or float number to integer, you can use (( $(bc <<< "...") )).
For example,
a=2.00
b=1
if (( $(bc <<<"$a > $b") )); then
echo "a is greater than b"
else
echo "a is not greater than b"
fi
You can include more than one comparison in the if statement. For example,
a=2.
b=1
c=1.0000
if (( $(bc <<<"$b == $c && $b < $a") )); then
echo "b is equal to c but less than a"
else
echo "b is either not equal to c and/or not less than a"
fi
That's helpful if you want to check if a numeric variable (integer or not) is within a numeric range.
One-line solution.
a=2
b=1
[[ ${a} -gt ${b} ]] && echo "true" || echo "false"
gt reference: https://www.gnu.org/software/bash/manual/html_node/Bash-Conditional-Expressions.html
&& reference: https://www.gnu.org/software/bash/manual/html_node/Shell-Arithmetic.html
[[...]] construct reference: https://www.gnu.org/software/bash/manual/bash.html#index-_005b_005b
${} reference: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02 (2.6.2)
The format for parameter expansion is as follows:
${expression}
where expression consists of all characters until the matching '}'.
Any '}' escaped by a or within a quoted string, and
characters in embedded arithmetic expansions, command substitutions,
and variable expansions, shall not be examined in determining the
matching '}'.
The simplest form for parameter expansion is:
${parameter}
This code can also compare floats. It is using AWK (it is not pure Bash). However, this shouldn't be a problem, as AWK is a standard POSIX command that is most likely shipped by default with your operating system.
$ awk 'BEGIN {return_code=(-1.2345 == -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 >= -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 < -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
1
$ awk 'BEGIN {return_code=(-1.2345 < 2) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 > 2) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
To make it shorter for use, use this function:
compare_nums()
{
# Function to compare two numbers (float or integers) by using AWK.
# The function will not print anything, but it will return 0 (if the comparison is true) or 1
# (if the comparison is false) exit codes, so it can be used directly in shell one liners.
#############
### Usage ###
### Note that you have to enclose the comparison operator in quotes.
#############
# compare_nums 1 ">" 2 # returns false
# compare_nums 1.23 "<=" 2 # returns true
# compare_nums -1.238 "<=" -2 # returns false
#############################################
num1=$1
op=$2
num2=$3
E_BADARGS=65
# Make sure that the provided numbers are actually numbers.
if ! [[ $num1 =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then >&2 echo "$num1 is not a number"; return $E_BADARGS; fi
if ! [[ $num2 =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then >&2 echo "$num2 is not a number"; return $E_BADARGS; fi
# If you want to print the exit code as well (instead of only returning it), uncomment
# the awk line below and comment the uncommented one which is two lines below.
#awk 'BEGIN {print return_code=('$num1' '$op' '$num2') ? 0 : 1; exit} END {exit return_code}'
awk 'BEGIN {return_code=('$num1' '$op' '$num2') ? 0 : 1; exit} END {exit return_code}'
return_code=$?
return $return_code
}
$ compare_nums -1.2345 ">=" -1.2345 && echo true || echo false
true
$ compare_nums -1.2345 ">=" 23 && echo true || echo false
false
If you have floats, you can write a function and then use that. For example,
#!/bin/bash
function float_gt() {
perl -e "{if($1>$2){print 1} else {print 0}}"
}
x=3.14
y=5.20
if [ $(float_gt $x $y) == 1 ] ; then
echo "do stuff with x"
else
echo "do stuff with y"
fi
I solved this by using a small function to convert version strings to plain integer values that can be compared:
function versionToInt() {
local IFS=.
parts=($1)
let val=1000000*parts[0]+1000*parts[1]+parts[2]
echo $val
}
This makes two important assumptions:
The input is a "normal SemVer string"
Each part is between 0-999
For example
versionToInt 12.34.56 # --> 12034056
versionToInt 1.2.3 # --> 1002003
Example testing whether npm command meets the minimum requirement...
NPM_ACTUAL=$(versionToInt $(npm --version)) # Capture npm version
NPM_REQUIRED=$(versionToInt 4.3.0) # Desired version
if [ $NPM_ACTUAL \< $NPM_REQUIRED ]; then
echo "Please update to npm#latest"
exit 1
fi
Just adding to all the above answers:
If you have more than one expression in single if statement, you can do something like this:
if (( $a % 2 == 0 )) && (( $b % 2 != 0));
then
echo "What you want to do"
fi
Hope this helps!

Weird result in bash elif ladder [duplicate]

I'm unable to get numeric comparisons working:
echo "enter two numbers";
read a b;
echo "a=$a";
echo "b=$b";
if [ $a \> $b ];
then
echo "a is greater than b";
else
echo "b is greater than a";
fi;
The problem is that it compares the number from the first digit on, i.e., 9 is bigger than 10, but 1 is greater than 09.
How can I convert the numbers into a type to do a true comparison?
In Bash, you should do your check in an arithmetic context:
if (( a > b )); then
...
fi
For POSIX shells that don't support (()), you can use -lt and -gt.
if [ "$a" -gt "$b" ]; then
...
fi
You can get a full list of comparison operators with help test or man test.
Like this:
#!/bin/bash
a=2462620
b=2462620
if [ "$a" -eq "$b" ]; then
echo "They're equal";
fi
Integers can be compared with these operators:
-eq # Equal
-ne # Not equal
-lt # Less than
-le # Less than or equal
-gt # Greater than
-ge # Greater than or equal
See this cheatsheet.
There is also one nice thing some people might not know about:
echo $(( a < b ? a : b ))
This code will print the smallest number out of a and b
In Bash I prefer doing this as it addresses itself more as a conditional operation unlike using (( )) which is more of arithmetic.
[[ n -gt m ]]
Unless I do complex stuff like
(( (n + 1) > m ))
But everyone just has their own preferences. Sad thing is that some people impose their unofficial standards.
You can also do this:
[[ 'n + 1' -gt m ]]
Which allows you to add something else which you could do with [[ ]] besides arithmetic stuff.
The bracket stuff (e.g., [[ $a -gt $b ]] or (( $a > $b )) ) isn't enough if you want to use float numbers as well; it would report a syntax error. If you want to compare float numbers or float number to integer, you can use (( $(bc <<< "...") )).
For example,
a=2.00
b=1
if (( $(bc <<<"$a > $b") )); then
echo "a is greater than b"
else
echo "a is not greater than b"
fi
You can include more than one comparison in the if statement. For example,
a=2.
b=1
c=1.0000
if (( $(bc <<<"$b == $c && $b < $a") )); then
echo "b is equal to c but less than a"
else
echo "b is either not equal to c and/or not less than a"
fi
That's helpful if you want to check if a numeric variable (integer or not) is within a numeric range.
One-line solution.
a=2
b=1
[[ ${a} -gt ${b} ]] && echo "true" || echo "false"
gt reference: https://www.gnu.org/software/bash/manual/html_node/Bash-Conditional-Expressions.html
&& reference: https://www.gnu.org/software/bash/manual/html_node/Shell-Arithmetic.html
[[...]] construct reference: https://www.gnu.org/software/bash/manual/bash.html#index-_005b_005b
${} reference: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02 (2.6.2)
The format for parameter expansion is as follows:
${expression}
where expression consists of all characters until the matching '}'.
Any '}' escaped by a or within a quoted string, and
characters in embedded arithmetic expansions, command substitutions,
and variable expansions, shall not be examined in determining the
matching '}'.
The simplest form for parameter expansion is:
${parameter}
This code can also compare floats. It is using AWK (it is not pure Bash). However, this shouldn't be a problem, as AWK is a standard POSIX command that is most likely shipped by default with your operating system.
$ awk 'BEGIN {return_code=(-1.2345 == -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 >= -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 < -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
1
$ awk 'BEGIN {return_code=(-1.2345 < 2) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 > 2) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
To make it shorter for use, use this function:
compare_nums()
{
# Function to compare two numbers (float or integers) by using AWK.
# The function will not print anything, but it will return 0 (if the comparison is true) or 1
# (if the comparison is false) exit codes, so it can be used directly in shell one liners.
#############
### Usage ###
### Note that you have to enclose the comparison operator in quotes.
#############
# compare_nums 1 ">" 2 # returns false
# compare_nums 1.23 "<=" 2 # returns true
# compare_nums -1.238 "<=" -2 # returns false
#############################################
num1=$1
op=$2
num2=$3
E_BADARGS=65
# Make sure that the provided numbers are actually numbers.
if ! [[ $num1 =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then >&2 echo "$num1 is not a number"; return $E_BADARGS; fi
if ! [[ $num2 =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then >&2 echo "$num2 is not a number"; return $E_BADARGS; fi
# If you want to print the exit code as well (instead of only returning it), uncomment
# the awk line below and comment the uncommented one which is two lines below.
#awk 'BEGIN {print return_code=('$num1' '$op' '$num2') ? 0 : 1; exit} END {exit return_code}'
awk 'BEGIN {return_code=('$num1' '$op' '$num2') ? 0 : 1; exit} END {exit return_code}'
return_code=$?
return $return_code
}
$ compare_nums -1.2345 ">=" -1.2345 && echo true || echo false
true
$ compare_nums -1.2345 ">=" 23 && echo true || echo false
false
If you have floats, you can write a function and then use that. For example,
#!/bin/bash
function float_gt() {
perl -e "{if($1>$2){print 1} else {print 0}}"
}
x=3.14
y=5.20
if [ $(float_gt $x $y) == 1 ] ; then
echo "do stuff with x"
else
echo "do stuff with y"
fi
I solved this by using a small function to convert version strings to plain integer values that can be compared:
function versionToInt() {
local IFS=.
parts=($1)
let val=1000000*parts[0]+1000*parts[1]+parts[2]
echo $val
}
This makes two important assumptions:
The input is a "normal SemVer string"
Each part is between 0-999
For example
versionToInt 12.34.56 # --> 12034056
versionToInt 1.2.3 # --> 1002003
Example testing whether npm command meets the minimum requirement...
NPM_ACTUAL=$(versionToInt $(npm --version)) # Capture npm version
NPM_REQUIRED=$(versionToInt 4.3.0) # Desired version
if [ $NPM_ACTUAL \< $NPM_REQUIRED ]; then
echo "Please update to npm#latest"
exit 1
fi
Just adding to all the above answers:
If you have more than one expression in single if statement, you can do something like this:
if (( $a % 2 == 0 )) && (( $b % 2 != 0));
then
echo "What you want to do"
fi
Hope this helps!

checking if a string is a palindrome

I am trying to check if a string is a palindrome in bash. Here is what I came up with:
#!/bin/bash
read -p "Enter a string: " string
if [[ $string|rev == $string ]]; then
echo "Palindrome"
fi
Now, echo $string|rev gives reversed string. My logic was to use it in the condition for if. That did not work out so well.
So, how can I store the "returned value" from rev into a variable? or use it directly in a condition?
Another variation without echo and unnecessary quoting within [[ ... ]]:
#!/bin/bash
read -p "Enter a string: " string
if [[ $(rev <<< "$string") == "$string" ]]; then
echo Palindrome
fi
A bash-only implementation:
is_palindrome () {
local word=$1
local len=$((${#word} - 1))
local i
for ((i=0; i <= (len/2); i++)); do
[[ ${word:i:1} == ${word:len-i:1} ]] || return 1
done
return 0
}
for word in hello kayak; do
if is_palindrome $word; then
echo $word is a palindrome
else
echo $word is NOT a palindrome
fi
done
Inspired by gniourf_gniourf:
is_palindrome() {
(( ${#1} <= 1 )) && return 0
[[ ${1:0:1} != ${1: -1} ]] && return 1
is_palindrome ${1:1: 1}
}
I bet the performance of this truly recursive call really sucks.
Use $(command substitution):
#!/bin/bash
read -p "Enter a string: " string
if [[ "$(echo "$string" | rev)" == "$string" ]]; then
echo "Palindrome"
fi
Maybe it is not the best implementation, but if you need something with pure sh
#!/bin/sh
#get character <str> <num_of_char>. Please, remember that indexing is from 1
get_character() {
echo "$1" | cut -c "$2"
}
for i in $(seq $((${#1} / 2))); do
if [ "$(get_character "$1" "$i")" != "$(get_character "$1" $((${#1} - i + 1)))" ]; then
echo "NO"
exit 0
fi
done
echo "YES"
and canonical way with bash as well
for i in $(seq 0 $((${#1} / 2 - 1))); do
if [ "${1:$i:1}" != "${1:$((${#1} - i - 1)):1}" ]; then
echo "NO"
exit 0
fi
done
echo "YES"
Skipping all punctuation marks and letter case.
input:He lived as a devil, eh?
output:Palindrome
input:Madam, I am Adam.
output:Not Palindrome
#!/bin/bash
#set -x
read -p "Enter a sentence" message
message=$(echo "$message" | \
sed -e '
s/[[:space:]]//g
s/[[:punct:]]//g
s/\!//g
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
' )
i=0
while read -n 1 letter
do
tempArray[i]="$letter"
((i++))
done < <(echo "$message")
i=0
counter=$((${#message}-1))
while [ "$i" -ne $((${#message}/2)) ]
do
if [ "${tempArray[$i]}" = "${tempArray[$counter]}" ]
then
((i++))
((counter--))
else echo -n "Not ";break
fi
done
echo "Palindrome"
exit

if [ awk 'BEGIN{print 0.4*10}' > 1 -eq 0 ]; then echo YES; fi

Please help. I'm getting an error when doing this. How ca I fix
if [ awk 'BEGIN{print 0.4*10}' > 1 -eq 1 ]; then echo YES; fi
shell syntax is tricky to internalize at first. Take a moment to realize that the if command doesn't really require any special syntax: it makes its decision based on the exit status of the command. Usually you'll see square brackets, but they are really just aliases for the test command (more or less).
If you want to work on awk's exit status:
if awk 'BEGIN {n = 0.4 * 10; exit (!(n>1))}'; then echo yes; fi
If you just want to do some math:
result=$(bc <<< "(0.4 * 10) > 1")
if [[ $result = "1" ]]; then echo "YES"; fi
# or, in one step
if [[ $(bc <<< "(0.4 * 10) > 1") = "1" ]]; then echo "YES"; fi
From a bash prompt, type help if
Your command has syntax errors. You probably meant to do maths comparison in awk itself and anyway BASH cannot do non-integer maths.
May be you meant this:
if [ $(awk 'BEGIN{print ( (0.4*10) > 1) }') -eq 1 ]; then echo YES; fi
YES
Something like that would work:
if [[ $(awk 'BEGIN{print 0.4*10}') > 1 ]]; then
echo YES
fi
But it is not at all clear what you intend to do.
You do not need the if. This will print yes if calculation is 4 or no if not.
[[ $(awk 'BEGIN{print (0.4*10) }') -eq 4 ]] && echo "yes" || echo "no"
or
[ $(awk 'BEGIN{print (0.4*10) }') -eq 4 ] && echo "yes" || echo "no"

ksh + compare numbers – two ways

the following example shows hot to compare numbers
I give here two different ways
one way with the ">" and "<"
and second way with "-gt" or "-lt"
both ways are work exactly
so what the differences between them ? or maybe there are not difference ?
example 1
ksh
a=1
b=2
[[ $a > $b ]] && echo ok
[[ $a < $b ]] && echo ok
ok
example 2
ksh
a=1
b=2
[[ $a -gt $b ]] && echo ok
[[ $a -lt $b ]] && echo ok
ok
In your examples there are no difference, but that is just an unfortunate choice of values for a and b.
-lt, -gt are for numeric comparison
< and > are for alphabetic comparison
$ a=12
$ b=6
$ [[ $a -lt $b ]] && echo ok
$ [[ $a &lt $b ]] && echo ok
ok

Resources