I want to do an if statement with three conditions that have to be satisfied at the same time. I am using Ubuntu Bash for Windows and the values $c1, $c2 and $c3 are non-integer (decimal negative numbers).
if [ (( $(echo "$c1 < 0" | bc -l) )) ] && [ (( $(echo "$c2 < 0" | bc -l) )) ] && [ (( $(echo "$c3 < 0" | bc -l) )) ];
then
>&2 echo -e " ++ Constraints OK"
else
>&2 echo -e " ++ Constraints WRONG"
fi
However, I get the following syntax error in the if line: syntax error near unexpected token `('
If I just put one condition:
if (( $(echo "$c1 < 0" | bc -l) ));
it works, but when I add the three of them as AND (&&), I get the error. Can anyone help me?
Considerably more efficient (assuming you know your values are numbers, and only need to check whether they're all negative) would be:
if [[ $c1 = -* ]] && [[ $c2 = -* ]] && [[ $c3 = -* ]]; then
>&2 echo " ++ Constraints OK"
else
>&2 echo " ++ Constraints WRONG"
fi
If you want to be more specific about the permissible formats (f/e, allowing leading spaces), a regex is another option, which similarly can be implemented more efficiently than spawning a series of subshells invoking bc:
nnum_re='^[[:space:]]*-([[:digit:]]*[.][[:digit:]]+|[[:digit:]]+)$'
if [[ $c1 =~ $nnum_re ]] && [[ $c2 =~ $nnum_re ]] && [[ $c3 =~ $nnum_re ]]; then
>&2 echo " ++ Constraints OK"
else
>&2 echo " ++ Constraints WRONG"
fi
First, pass the relational AND operators into bc to get rid of some punctuation (also only invokes bc once):
if (( $(echo "$c1 < 0 && $c2 < 0 && $c3 < 0" | bc -l) == 1 ))
then
>&2 echo -e " ++ Constraints OK"
else
>&2 echo -e " ++ Constraints WRONG"
fi
Although if it were me, I would create a shell function returning a "true" exit status if bc evaluates the result of an expression to non-zero. Then you can hide most of the ugly punctuation in one place separated from your main logic:
function bc_true() {
(( $(echo "$#" | bc -l) != 0 ))
}
And write a (IMO) cleaner shell expression:
if bc_true "$c1 < 0 && $c2 < 0 && $c3 < 0"
then
...
Related
I wanna Write a Bash script that can print if the number in the last column is odd or even or if no numbers in the line from a text file, the data is looking like this in a db.txt file :
sdn sddjk#gmail 123
ksd 234
sddd sddsd#gmail
i tried this :
#!/bin/bash
input="db.txt"
while IFS=" " read -r rec_column3
do
if [ $((number % 2)) -eq 0 ]; then
echo even
elif [ $((number % 2)) -eq 1 ]; then
echo odd
elif [[ "$rec_column3" != "number" ]]; then
echo not number
else
echo not found
fi
done
output is :
even
even
so can anyone helps me ? tnx
#!/bin/bash
input="db.txt"
#########################
# check third field
#########################
echo "check third field"
while read -r _ _ rec_column3
do
if [[ -z "$rec_column3" ]]; then
echo "not found" >&2;
elif ! [[ $rec_column3 =~ ^[0-9]+$ ]] ; then
echo "'$rec_column3' is not a number" >&2;
elif [[ $((rec_column3 % 2)) -eq 0 ]]; then
echo "'$rec_column3' is even" >&2
else
echo "'$rec_column3' is odd" >&2
fi
done < $input
echo "-----------------------"
#########################
# or check last field
#########################
echo "check last field"
while IFS=' ' read -r -a array
do
last_column=""
[[ ${#array[#]} -ne 0 ]] && last_column=${array[-1]}
if [[ -z "$last_column" ]]; then
echo "not found" >&2
elif ! [[ $last_column =~ ^[0-9]+$ ]] ; then
echo "'$last_column' is not a number" >&2
elif [[ $((last_column % 2)) -eq 0 ]]; then
echo "'$last_column' is even" >&2
else
echo "'$last_column' is odd" >&2
fi
done < $input
$ cat db.txt
sdn sddjk#gmail 123
ksd 234
ksd
12345
sddd sddsd#gmail 234
sddd sddsd#gmail 111
sddd sddsd#gmail aaa
$ ./script.sh
check third field
'123' is odd
not found
not found
not found
not found
'234' is even
'111' is odd
'aaa' is not a number
-----------------------
check last field
'123' is odd
'234' is even
'ksd' is not a number
'12345' is odd
not found
'234' is even
'111' is odd
'aaa' is not a number
awk is probably a better tool for this job. You can do something like this
awk 'BEGIN {split("even odd", a)} $NF ~ /^[0-9]+$/ {print a[$NF%2+1]; next} {print "NAN"}' db.txt
Checks if the last field is odd or even (the +1 is because the array a is 1-based).
I have stuck with my bash code. and needed some help.
I trying to write bash code that :
Length – minimum of 10 characters.
Contain both alphabet and number.
Include both the small and capital case letters.
Color the output green if it passed the validation and red if it didn’t.
Return exit code 0 if it passed the validation and exit code 1 if it didn’t.,
the problem that can be resolved for me is that code does not check upper and lower case.
and have exit 0, which means the password ok in all cases.
# Color for Output
Warning='\033[0;31m' # Red
Success='\033[0;32m' # Green
NC="\033[0m" # No Color
password=$1
len="${#password}"
if test $len -ge 4 ; then
echo "$password" | grep -eq [[:digit:]]
if test $? -eq 1 ; then
echo "$password" | grep -e [[:upper:]]
if test $? -e 1 ; then
echo "$password" | grep -e [[:lower:]]
if test $? -e 1 ; then
printf "${Success}Strong password!$1\n"
else
echo "weak password include lower case char"
fi
else
echo "weak password include capital char"
fi
else
echo && echo $?
printf "${Success}Strong password!$1\n"
fi
else
echo $?
printf ${Warning}"Try again... \nPassword must have at least 10 characters.$1\n"
fi
In bash, within the [[ ... ]] conditional, the == and != operators do pattern matching. And bash patterns can include POSIX character classes.
error() {
echo "$*" >&2
exit 1
}
password=$1
if (( ${#password} < 10 )); then
error "Too short"
elif [[ $password != *[[:digit:]]* ]]; then
error "Does not contain a digit"
elif [[ $password != *[[:lower:]]* ]]; then
error "Does not contain a lower case letter"
elif [[ $password != *[[:upper:]]* ]]; then
error "Does not contain an upper case letter"
fi
echo "OK"
exit 0
The list of POSIX character classes is documented in the manual at 3.5.8.1 Pattern Matching
That cascading-if can be written more concisely:
(( ${#password} >= 10 )) || error "Too short"
[[ $password == *[[:digit:]]* ]] || error "Does not contain a digit"
[[ $password == *[[:lower:]]* ]] || error "Does not contain a lower case letter"
[[ $password == *[[:upper:]]* ]] || error "Does not contain an upper case letter"
echo OK
[[ ... ]] also has the =~ regular-expression matching operator (there is no !~ negated matching operator):
# it contains a digit, or error
[[ $password =~ [[:digit:]] ]] || error "Does not contain a digit"
read X;
read Y;
read Z;
if [[ $X,$Y,$Z -ne "0" ]]; then
if [[ $X,$Y,$Z -ge 1 && $X,$Y,$Z -le 1000 ]] && [[ $((X+Y)) -gt $Z || $((Y+Z)) -gt $X || $((X+Z)) -gt $Y ]]; then
case $X,$Y,$Z in
$X -eq $Y && $Y -eq $Z && $X -eq $Z ) echo "EQUILATERAL";;
esac
else
echo "bye";
fi
fi
*./bashtesting.sh: line 7: syntax error near unexpected token `-eq
./bashtesting.sh: line 7: $X -eq $Y && $Y -eq $Z && $X -eq $Z ) echo "EQUILATERAL";;*
how to equate the three variables at a time?
This is how I would do it, with a loop and associative array.
#!/usr/bin/env bash
for h in x y z; do
read -rp 'Enter input: ' input
if [[ -z $input ]]; then
printf >&2 '%s is empty, please try again!\n' "${input:-input}"
exit 1
elif [[ $input != *[0-9]* ]]; then
printf '%s is not an int, please try again!\n' "$input"
exit 1
else
declare -A num[$h]=$input
fi
done
for i in "${num[#]}"; do
if ! (( i == 0 )); then
for j in "${num[#]}"; do
if (( j > 1 && j < 100 )); then
if (( (num[x] + num[y]) > num[z] || (num[y] + num[z]) > num[x] || (num[x] + num[z]) > num[y] )); then
if (( num[x] == num[y] && num[y] == num[x] && num[x] == num[z] )); then
echo equilateral
break 2
fi
fi
fi
done
fi
done
It it not pretty, It looks like an Anti Pattern, and there might be a better way to do it but this will get you there I suppose.
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
Why this works:
Output=$( tail --lines=1 $fileDiProva )
##[INFO]Output = "OK"
if [[ $Output == $OK ]]; then
echo "OK"
else
echo "No Match"
fi
and this not?
Output=$( tail --lines=1 $fileDiProva )
##[INFO]Output = "OK"
if [[ $Output -eq $OK ]]; then
echo "OK"
else
echo "No Match"
fi
What's the difference?? between == and -eq?
Thanks!
-eq is an arithmetic test.
You are comparing strings.
From help test:
Other operators:
arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne,
-lt, -le, -gt, or -ge.
When you use [[ and use -eq as the operator, the shell attempts to evaluate the LHS and RHS. The following example would explain it:
$ foo=something
+ foo=something
$ bar=other
+ bar=other
$ [[ $foo -eq $bar ]] && echo y
+ [[ something -eq other ]]
+ echo y
y
$ something=42
+ something=42
$ [[ $foo -eq $bar ]] && echo y
+ [[ something -eq other ]]
$ other=42
+ other=42
$ [[ $foo -eq $bar ]] && echo y
+ [[ something -eq other ]]
+ echo y
y
Have a look at this explanation of if.
The first one == is in the section of string compare operators and it can only compare two strings.
The second one -eq is in the last section ARG1 OP ARG2(last one) and its documentation says "ARG1" and "ARG2" are integers.
-eq, -lt, -gt is only used for arithmetic value comparison(integers).
== is used for string comparison.