I have this code. But I cant get the result i need.
Im comparing variables in bash.
If the number i get from a webpage is greater than 40 i want a yes.
var2=40
maj=$(curl $1)
var1=$(echo "$maj" | grep "[0-9]" | awk '{print $3}')
echo $var1
if [[ "$var1" > "$var2" ]]; then
echo "yes"
else
echo "no"
fi
$1 could be:
http://pastebin.com/raw.php?i=heH8s5yy
http://pastebin.com/raw.php?i=k5dkKUu1
http://pastebin.com/raw.php?i=59V0eJmz
the thing is when i do
./test.sh http://pastebin.com/raw.php?i=k5dkKUu1
i get yes
and 5 is less than 40
This is because the > symbol inside double brackets [[ ... ]] does lexicographical comparison. You need to use the -gt operator to compare numerical values, like this:
[[ $var1 -gt "$var2" ]]
It is even better to do numeric operations inside double parenthesis, like this
if (( var1 > var2 ))
Related
I have this variable:
>echo $br_name
srxa wan-a1 br-wan3-xa1 0A:AA:DD:C1:F1:A3 ge-0.0.3 srxa wan-a2 br-wan3-xa2 0A:AA:DD:C1:F2:A3 ge-0.0.3
I am trying to create a conditional where it detects whether ge-0.0.3 is repeated more than 1 time in my variable $br_name
For example:
if [[ $br_name has ge-0.0.3 repeated more than one time ]]
then
echo "ge-0.0.3 is shown more than once"
else
:
fi
Bash's =~ is using extended RE.
[Bash-5.2] % check() { local s='(ge-0\.0\.3.*){2,}'; [[ "$1" =~ $s ]] && echo yes || echo no; }
[Bash-5.2] % check 'xxx'
no
[Bash-5.2] % check 'ge-0.0.3'
no
[Bash-5.2] % check 'ge-0.0.3 ge-0.0.3 '
yes
[Bash-5.2] % check 'ge-0.0.3 ge-0.0.3 ge-0.0.3 '
yes
You can use grep -o to print only the matched phrase. Also use -F to make sure that it matches literal characters instead of a regex where . and - are special
if [[ $(echo "$br_name" | grep -Fo ge-0.0.3 | wc -l) -gt 1 ]]; then
echo "ge-0.0.3 is shown more than once"
else
echo "only once"
fi
For more complex patterns of course you can drop -F and write a proper regex for grep
simple word
If your word would be "easy", you can detect the occurrences count with:
echo "123 123 123" | sed "s/123 /123\n/g" | wc -l
In which the word is replace for the same but with \n and then wc count the lines
or you can try one of these:
Count occurrences of a char in a string using Bash
How to count number of words from String using shell
complex
Since your word is "complex" or you will want a pattern, you will need regex:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
https://linuxconfig.org/advanced-bash-regex-with-examples
script.sh
count=0
for word in $1; do
if [[ "$word" =~ .*ge-0\.0\.3.* ]]
then
count=$(($count+1))
fi
done
if [ "$count" -gt "1" ];
then
echo "ge-0.0.3 is shown more than once"
else
if [ "$count" -eq "0" ];
then
echo "ge-0.0.3 is not shown"
else
echo "ge-0.0.3 is shown once"
fi
fi
execution
bash script.sh "srxa wan-a1 br-wan3-xa1 0A:AA:DD:C1:F1:A3 ge-0.0.3 srxa wan-a2 br-wan3-xa2 0A:AA:DD:C1:F2:A3 ge-0.0.3"
grep
With grep you can get the ocurrence count
ocurrences=( $(grep -oE '(ge-0\.0\.3)' <<<$1) )
ocurrences_count=${#ocurrences[*]}
echo $ocurrences_count
Assuming you want to do a literal string match on whole words, not substrings, then this might be what you want:
$ if (( $(grep -oFw 'ge-0.0.3' <<<"$br_name" | wc -l) > 1 )); then echo 'yes'; else echo 'no'; fi
yes
I am taking baby steps at learning bash and I am developing a piece of code which takes an input and checks if it contains any spaces. The idea is that the variable should NOT contain any spaces and the code should consequently echo a suitable message.
Try this:
#!/bin/bash
if [[ $1 = *[[:space:]]* ]]
then
echo "space exist"
fi
You can use grep, like this:
echo " foo" | grep '\s' -c
# 1
echo "foo" | grep '\s' -c
# 0
Or you may use something like this:
s=' foo'
if [[ $s =~ " " ]]; then
echo 'contains space'
else
echo 'ok'
fi
You can test simple glob patterns in portable shell by using case, without needing any external programs or Bash extensions (that's a good thing, because then your scripts are useful to more people).
#!/bin/sh
case "$1" in
*' '*)
printf 'Invalid argument %s (contains space)\n' "$1" >&2
exit 1
;;
esac
You might want to include other whitespace characters in your check - in which case, use *[[:space:]]* as the pattern instead of *' '*.
You can use wc -w command to check if there are any words. If the result of this output is a number greater than 1, then it means that there are more than 1 words in the input. Here's an example:
#!/bin/bash
read var1
var2=`echo $var1 | wc -w`
if [ $var2 -gt 1 ]
then
echo "Spaces"
else
echo "No spaces"
fi
Note: there is a | (pipe symbol) which means that the result of echo $var1 will be given as input to wc -w via the pipe.
Here is the link where I tested the above code: https://ideone.com/aKJdyN
You could use parameter expansion to remove everything that isn't a space and see if what's left is the empty string or not:
var1='has space'
var2='nospace'
for var in "$var1" "$var2"; do
if [[ ${var//[^[:space:]]} ]]; then
echo "'$var' contains a space"
fi
done
The key is [[ ${var//[^[:space:]]} ]]:
With ${var//[^[:space:]]}, everything that isn't a space is removed from the expansion of $var.
[[ string ]] has a non-zero exit status if string is empty. It's a shorthand for the equivalent [[ -n string ]].
We could also quote the expansion of ${var//[^[:space:]]}, but [[ ... ]] takes care of the quoting for us.
It seems like bash's maximum signed integer value is 9223372036854775807 (2^63)-1. Is there a way for bash to handle larger values than this? I need to handle numbers up to 10000000000000000000000000001, but I'm not sure how to accomplish this in bash.
A=10000000000000000000000000000
echo $A
10000000000000000000000000000
let A+=1
echo $A
4477988020393345025
EDIT
Thanks Benjamin W. for your comment. Based on that I am trying the following strategy. Are there any perceived issues with this? Meaning, aside from some performance hit due to invoking bc, would there by complications from using bc to increment my variable?
A=10000000000000000000000000000
echo $A
10000000000000000000000000000
A=$(bc <<< "$A+1")
echo $A
10000000000000000000000000001
Also, I've tested some bash operations (greater than, less than, etc) and it seems it behaves as expected. E.g.:
A=10000000000000000000000000000
echo $A
10000000000000000000000000000
[[ "$A" -gt 10000000000000000000000000000 ]] && echo "A is bigger than 10000000000000000000000000000"
A=$(bc <<< "$A+1")
echo $A
10000000000000000000000000001
[[ "$A" -gt 10000000000000000000000000000 ]] && echo "A is bigger than 10000000000000000000000000000"
A is bigger than 10000000000000000000000000000
I'd recommend using bc with its arbitrary precision.
Bash overflows at 263:
$ A=$(( 2**63 - 1 ))
$ echo $A
9223372036854775807
$ echo $(( A+1 ))
-9223372036854775808
bc can handle this:
$ bc <<< "$A+1"
9223372036854775808
These numbers have to be handled with bc for everything from now on, though. Using [[ ]], they don't seem to overflow, but comparison doesn't work properly:
$ B=$(bc <<< "$A+1")
$ echo $B
9223372036854775808
$ set -vx
$ [[ $B -gt -$A ]] && echo true
[[ $B -gt -$A ]] && echo true
+ [[ 9223372036854775808 -gt -9223372036854775807 ]]
And in arithmetic context (( )), they overflow:
$ echo $(( B ))
-9223372036854775808
so the comparison doesn't work either:
$ (( B > A )) && echo true || echo false
false
Handling them with bc:
$ bc <<< "$B > $A"
1
and since within (( )) non-zero results evaluate to true and zero to false, we can use
$ (( $(bc <<< "$B > $A") )) && echo true
true
temp.txt is a file with a single line:
1.2034 3.2323 4.3121 5.1223
I have to do the following set of operations multiple times with different values so I have it all in a loop. Given below is the main issue I am facing and would appreciate some help solving.
v1=$(cut -d" " -f2 temp.txt);
v2=$(cut -d" " -f3 temp.txt);
v3=$(cut -d" " -f4 temp.txt);
v4=$(cut -d" " -f5 temp.txt);
#$v1, $v2, $v3, $v4 contain the above 4 values (1.2034, 3.2323, 4.3121, 5.1223). I have verified that. I want to compare their values, but when I do, I get an error because of incorrect syntax.
if [ "$v1" -gt "$v2" | bc ] && [ "$v3" -gt "$v4" | bc ]; then
echo Yes
fi
I get this error:
line 6: [: missing `]'
File ] is unavailable.
Could someone please help me with my syntax? I have tried a few different things already and it didn't work. I have tried different combinations of brackets and spaces and both using bc and without bc.
You want to construct a valid bc expression and pipe it to bc, capture the output, then compare that to the expected result.
if [[ $(echo "$v1 > $v2" | bc) == 1 && $(echo "$v3 > $v4" | bc) == 1 ]]; then
echo Yes
fi
If bash could do floating-point arithmetic (or if your values were actually integers), you could do the comparisons directly in bash:
if [[ $v1 -gt $v2 && $v3 -gt $v4 ]]; then echo Yes; fi
or
if (( v1 > v2 && v3 > v4 )); then echo Yes; fi
A quicker way to populate your four variables is
read x1 v1 v2 v3 v4 x2 < temp.txt
(x1 and x2 are just variables whose value we don't really care about; the first field and any fields that might occur after the 5th thone.)
I have a bash script in which I am attempting to compare a variable containing a whole number
VAR1=1
The real number to compare to, can be a decimal
VAR2=1.5
When I try:
if [[ $VAR1 -ge $VAR2]];
I am presented with a syntax error: invalid arithmetic operator
The problem is, when I try the >= string comparison, the result is always false irregardles of whether it logically is or not.
My question is, how can I fix this and do the arithmatic comparison?
Code Block
if [ $(bc -l <<<"$CPUUSAGE >= $MAXCPU") || $(bc -l <<<"$FREEMEM <= $MAXMEM") || $NUMHTTPD -ge $MAXHTTPD || $NUMMYSQL -ge $MAXMYSQL || $NUMPROCS -ge $MAXPROCESSES ]];
then
SendMessage;
sync ; echo 3 > /proc/sys/vm/drop_caches;
echo "Message Sent";
fi;
Bash doesn't support floating point numbers.
Try bc:
(( $(bc -l <<<"$v1 >= $v2") )) && echo "v1 is greater than or equal to v2"
I have used some bashisms here, notably the (( arithmetic context )) and <<< as an alternative to echoing the string to bc. The output of bc will be 1 or 0, depending on whether the statement is true or false. The message will only be echoed if the result is true.
The -l switch is shorthand for --mathlib, which as hek2mgl rightly asserts, is needed when working with floating point numbers.
If you want a fully-fledged if statement, you can do that as well:
if (( $(bc -l <<<"$v1 >= $v2") )); then
echo "v1 is greater than or equal to v2"
else
echo "v1 is less than v2"
fi
For the example in your question, you could use this:
if (( $(bc -l <<<"$CPUUSAGE >= $MAXCPU || $FREEMEM <= $MAXMEM") )) || [[ $NUMHTTPD -ge $MAXHTTPD || $NUMMYSQL -ge $MAXMYSQL || $NUMPROCS -ge $MAXPROCESSES ]]; then echo; fi
I've combined the two conditions in bc to save you calling the tool twice. I've also wrapped that part in an arithmetic context and used an extended test [[ for the rest.
bash does not support floating point operations. You could use bc for that:
if [ $(bc --mathlib <<< "$var1 >= $var2") = "1" ] ; then
echo "$var2 is greater than or equal to $var2"
fi
Note that unless you pass the --mathlib option, even bc would not support floating point operations.
AWK can do the trick too:
#!/bin/sh
VAR1=1
VAR2=1.5
if awk "BEGIN {exit $VAR1 >= $VAR2 ? 0 : 1}"
then
echo "$VAR1 is greater than or equal to $VAR2"
else
echo "$VAR2 is greater than or equal to $VAR1"
fi