Please see below example test code that I am getting false positive for:
#!/bin/bash
sub1(){
if [[ (($1 -lt 0) || "$1" == "-0") && ($3 =~ re) ]]
then
echo "Sub1: Condition is true $1 $2 $3 $4"
else
echo "Sub1: Condition is false $1 $2 $3 $4"
fi
}
sub2(){
if [[ (($1 -lt 0) || ("$1" == "-0")) && ($3 -ge 0) ]]
then
echo "Sub2: Condition is true $1 $2 $3 $4"
else
echo "Sub2: Condition is false $1 $2 $3 $4"
fi
}
nz='^-0$'
re='^[1-9]+$'
ne='^-[1-9]+$'
A=-0
B=50
C=-0
D=55
sub1 "$A" "$B" "$C" "$D"
sub2 "$A" "$B" "$C" "$D"
I am trying to pass multiple values of C (ex. 0, -0, 1, -1 etc)
Function Sub1 I am trying to check conditions using regex.
Function Sub2 I am trying to check conditions using arithmetic and string conditions.
None of them gives error but both provides false positive when you test with all possible values of C.
Any idea what's wrong in If statement?
Related
Well i need to compare all the strings as arguments of this shell script and say if all of them are equals or not so i try this
#!/bin/bash
#Ejercicio_4
if [ $# -ne 6 ]
then
echo Número de argumentos incorrecto
else
if [ $1 == $2 == $3 == $4 == $5 == $6 ]
then
echo Son iguales
else
echo No todas las palabras son iguales
fi
fi
also i try thinks like $# == $1 but this didnt work :(
As long as you're using bash, and comparing text strings, the [[..]] test is safer and more flexible. You can use && and || inside them for and/or operators. So this would work :
#!/bin/bash
#Ejercicio_4
if [ $# -ne 6 ]
then
echo Número de argumentos incorrecto
else
if [[ "$1" == "$2" && "$1" == "$3" && "$1" == "$4" && "$1" == "$5" && "$1" == "$6" ]]
then
echo Son iguales
else
echo No todas las palabras son iguales
fi
fi
Note also that "==" is not actually valid syntax in the old "[" test, although bash accepts it. You should properly only use the single-character "=" in single-bracket tests.
If you're comparing integers, however, you should use ((..)) instead.
But I strongly suggest you not follow this method, since when the number of arguments increase you will have to include a lot more conditions in the if statement which could get cumbersome. So prefer a loop and check the first arg with all others and see if they're equal, like given below :
#!/usr/bin/env bash
if [ $# -ne 6 ]
then
echo "Number of arguments are lesser than required number 6"
else
number_of_elements=$#
arguments=("$#")
first=("$1")
for (( i=1; i<number_of_elements; i++ )); do
if [[ "$first" != ${arguments[i]} ]];then
break
fi
done
if [[ $i -eq $number_of_elements ]];then
echo "All the argument strings are equal"
else
echo "Not equal"
fi
fi
The operator for string equality usually is =. And such chains aren't possible either. Check your man test.
Normally you'd use -a for "and" and check for each argument individually.
...
if [ $1 = $2 -a $1 = $3 -a $1 = $4 -a $1 = $5 -a $1 = $6 ]; then
...
i have this function that accepts 3 parameters , ech one contain of 4 numbers and a capital letter for example : "1234A"
and i want to print 1 if the second parameter is bigger than the third one and smaller than the first one ,
i wrote this function that i cutted the 4 numbers in a parameter for each parameter and the letter in diffrent paramater for each one and i began to compare
but the problem it print nothing !!
anyone know how to do things in one if statement rather than two if statements ??
what i did :
function check {
curr_letter=`echo "$1" | cut -c5`
min_letter=`echo "$3" | cut -c5`
sm_letter=`echo "$2" | cut -c5`
curr_nums=`echo "$1" | cut -c1-4`
min_nums=`echo "$3" | cut -c1-4`
sm_nums=`echo "$2" | cut -c1-4`
if [[ sm_nums -eq curr_nums && sm_letter < curr_letter ]] ; then
if [[ sm_nums -eq min_nums && sm_letter > min_letter ]] ; then
echo 1
fi
if [[ sm_nums > min_nums ]] ; then
echo 1
fi
fi
if [[ sm_nums < curr_nums ]] ; then
if [[ sm_nums -eq min_nums && sm_letter > min_letter ]] ; then
echo 1
fi
if [[ sm_nums > min_nums ]] ; then
echo 1
fi
fi
}
i get nothing when i test this in bash , i get an empty line..
this is how i tested it :
p=`check "1617B" "1617A" "0000A"` echo $p
You can omit the $ in variable names within arithmetic context ((...)).
Within [[ ... ]] you cannot omit it.
Instead of calling echo ... | cut -c..., you can easily extract substrings using Bash's very own syntax {var:start:length}.
Within a [[ ... ]] or ((...)),
use == instead of -eq.
Note however that < and > operators sort lexicographically within a [[ ... ]], but numerically in arithmetic context ((...)).
Therefore the string-valued variables (named *_letter in your example)
should be compared within [[ ... ]], the numeric variables (named *_nums in your example) should be compared within ((...)).
Like this:
function check() {
curr_letter=${1:4:1}
min_letter=${3:4:1}
sm_letter=${2:4:1}
curr_nums=${1:0:4}
min_nums=${3:0:4}
sm_nums=${2:0:4}
if (( sm_nums == curr_nums )) && [[ $sm_letter < $curr_letter ]]; then
if (( sm_nums == min_nums )) && [[ $sm_letter > $min_letter ]] ; then
echo 1
fi
if (( sm_nums > min_nums )) ; then
echo 1
fi
fi
if (( sm_nums < curr_nums )) ; then
if (( sm_nums == min_nums )) && [[ $sm_letter > $min_letter ]] ; then
echo 1
fi
if (( sm_nums > min_nums )) ; then
echo 1
fi
fi
}
Lastly, instead of p=`check "1617B" "1617A" "0000A"`; echo $p,
better write like this:
echo $(check "1617B" "1617A" "0000A")
why not just
awk '$3 <= $2 && $2 <= $1 {print 1}'
or if you need a function
check() { awk '$3 <= $2 && $2 <= $1 {print 1}' <<< "$#"; }
or
check() { awk "BEGIN{if($3 <= $2 && $2 <= $1) print 1}"; }
I am learning about shell scripting, particularly the shell commands test, [ ]. [[ ]] and (( )), I wrote a real simple script to test my understanding, it takes in three arguments, if all three are integers (and the second argument is lower than the third) it tests if the first argument lays in the range between the second and third. Here it is:
#!/bin/bash
#test if arg1 is contained in the range [arg2, arg3]
function isint()
{
if [[ "$1" =~ ^-?[0-9]+$ ]]; then
echo $1 is integer
return 0
else
echo $1 is not integer
return 1
fi
}
if [[ $(isint $1) && $(isint $2) && $(isint $3) && $2 -lt $3 ]]; then
if [[ $2 -le $1 && $1 -le $3 ]]; then
echo "$1 is in the range [$2,$3]"
else
echo "$1 is not in the range [$2,$3]"
fi
else
echo usage: $0 INT1 INT2 INT3
fi
Here is my problem: the preceding script doesn't work, the
if [[ $(isint $1) && $(isint $2) && $(isint $3) && $2 -lt $3 ]]
never evaluates to true, if I want to obtain the wanted result I need to change the test to:
if [[ !$(isint $1) && !$(isint $2) && !$(isint $3) && $2 -lt $3 ]]
I don't get why this is working, the way I understood the testand [[ ]] commands is that they both evaluate the expressions passed to them, 0 evaluates to true and 1 evaluates to false. In my case if the correct parameters are passed to the script, the isint calls evaluate to 0, hence true, why the need for inverting them for the script to work as I want ?
Thank you in advance.
Your issue is the $() enclosing your calls to isint. These replace the command with the standard output of the call to isint, so in this case what you're really running is:
if [[ "$1 is integer" && "$2 is integer" && "$3 is integer" && $2 -lt $3 ]]
You want to use the return values instead, so just drop the $() and move the isint's out of the brackets:
if isint "$1" && isint "$2" && isint "$3" && [[ "$2" -lt "$3" ]]
I added some quotes as well: you really should have your arguments quoted throughout the whole thing.
You're mixing up literal values with return codes. It should be this:
isint "$1" && isint "$2" && isint "$3" && [[ "$2" -lt "$3" ]]
That says, run these four commands and see if they're all true (return code == 0).
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!
I'm getting an error
Syntax error: invalid arithmetic operator (error token is ".txt")
at the if statement line. I check words_in_line by doing echo $words_in_line and it outputs numbers, so I don't understand why I'm getting this error. How do I fix this?
#!/usr/bin/env bash
#Outputs the lines that match wordcount range specified by min, $1, and max, $2
function get_correct_lines_in_file() {
while read line ; do
words_in_line=$( echo "$line" | wc -w );
if [[ words_in_line -ge $1 ]] && [[ words_in_line -le $2 ]]; then #ERROR HERE
echo "$line" >> MARBLES.txt
fi
done < $1
}
#Check if $1 and $2 arguments exists- are NOT NULL
if [[ "$1" != "" ]] && [[ "$2" != "" ]]; then
for i in ${*:3}
do
#If is a file you can read
if [[ -r $i && -f $i ]]; then
echo "$i exists and is readable"
get_correct_lines_in_file "$i"
#If file doesn't exist
elif [[ ! -f $i ]]; then
echo $i >> FAILED.log
fi
done
fi
If you want your minimum and maximum values to be accessible in your function, you need to pass them through. Consider accepting three arguments in your function, and explicitly passing the function's arguments through:
get_correct_lines_in_file() {
local -a words
while read -r -a words ; do
words_in_line=${#words[#]};
if (( words_in_line >= $2 )) && (( words_in_line <= $3 )); then
printf '%s\n' "${words[*]}"
fi
done <"$1" >>MARBLES.txt
}
...and later, passing the filename to be the function's $1, the script's $1 to be the function's $2, and the script's $2 to be the function's $3:
get_correct_lines_in_file "$i" "$1" "$2"