Removing Leading 0 from a number causes value to change [duplicate] - bash

This question already has answers here:
Shell equality operators (=, ==, -eq)
(4 answers)
Closed 1 year ago.
I do not understand what is happening here, based on Convert string into integer in bash script - "Leading Zero" number error, I am able to convert the string into integer. I can even do addition. However, when I compare the numbers it gives me the wrong comparison. Refer to example below
Here's an example
array=(02 08 10 11 23 52)
for i in 0 1 2 3 4 5
do
if [[ ${array[i]#0} > 15 ]]
then
echo "${array[i]#0} is greater than 15"
else
echo "${array[i]#0} is less than 15"
fi
done
Output
2 is greater than 15
8 is greater than 15
10 is less than 15
11 is less than 15
23 is greater than 15
52 is greater than 15

Either change [[ ]] to (( )) to use > or change the operator to -gt to do integer comparisons inside of [[ ]]
First:
#!/bin/bash
array=(02 08 10 11 23 52)
for i in 0 1 2 3 4 5
do
if (( ${array[i]#0} > 15 ))
then
echo "${array[i]#0} is greater than 15"
else
echo "${array[i]#0} is less than 15"
fi
done
Second:
#!/bin/bash
array=(02 08 10 11 23 52)
for i in 0 1 2 3 4 5
do
if [[ ${array[i]#0} -gt 15 ]]
then
echo "${array[i]#0} is greater than 15"
else
echo "${array[i]#0} is less than 15"
fi
done
If you use < > == inside of [ ] or [[ ]] is a string comparison operator. The string "02" is in fact less than the string "15":
% [[ 02 > 15 ]]; echo $?
1 # '1' means false and '0' means true
BUT you are stripping the leading "0" with #0:
% s="02"
% echo ${s#0}
2
So now you are comparing the STRING "2" to the STRING "15":
% [[ 2 > 15 ]]; echo $?
0
% [[ ${s#0} > 15 ]]; echo $?
0
Which give the surprising result you are seeing...
See this answer for an expanded set of examples.

Related

Create variable within a range of numbers except a list of numbers

I have the following variable with a list of numbers
vlist="1 13 20 21 22 24 25 28 240 131 133 136 138 224"
In the next loop I want to input a number between 1 - 250 except the numbers in vlist
while :; do
echo -en 'Number : ' ; read -n3 cvip ; echo
[[ $cvip =~ ^[0-9]+$ ]] || { echo -e '\nSorry input a number between 1 and 239 except '$vlist'\n' ; continue; }
cvip=$(expr ${cvip} + 0)
if ((cvip >= 1 && cvip <= 250)); then break ; else echo -e '\nNumber out of Range, input a number between 1 and 239 except '$vlist'\n' ; fi
done
Ηow can I enter the list exception inside the if statement range
If using bash, one approach is to store the bad numbers as keys in an associative array and see if that particular key exists when validating a number:
#!/usr/bin/env bash
vlist="1 13 20 21 22 24 25 28 240 131 133 136 138 224"
declare -A nums
for num in $vlist; do
nums[$num]=1
done
while true; do
while read -p "Number: " -r num; do
if [[ ! $num =~ ^[[:digit:]]+$ ]]; then
echo "Input an integer."
elif [[ ${nums[$num]:-0} -eq 1 ]]; then
echo "Input a number between 1 and 250 except '$vlist'"
elif [[ $num -lt 1 || $num -gt 250 ]]; then
echo "Input an integer between 1 and 250"
else
break
fi
done
printf "You successfully inputted %d\n" "$num"
done
The important bit is ${nums[$num]:-0}, which expands to the value of that element of the associative array if that key exists and is not null, or 0 otherwise. As noted by Glenn in a comment, in bash 4.3 or newer, [[ -v nums[$num] ]] works too for testing to see if a given key exists, so [[ ${nums[$num]:-0} -eq 1 ]] in the above could be replaced with it.

Bash nested conditionals show unexpected behavior

please take a look at this:
This should echo X10, but echoes jackpot... can anyone see why it doesn't behave as it should?
Probably just some mistake I made that do not throw errors?
dice1=1
dice2=40
#These two lines are just tests to see if my brain still function:
echo "Is dice 1 less than 2? $(($dice1 < 2))"
echo "Is dice 2 between 6 and 54? $(($dice2 > 5 && $dice2 < 55))"
if [[ $dice1 == 1 ]]
then
if [[ $dice2 < 6 ]]
then
#dice1 has to be equal 1 and dice2 less than 6:
echo "jackpot"
else
#Since dice2 is larger than 5, if smaller than 55
#it should be between 6 and 54...
if [[ $dice2 < 55 ]]
then
echo "X10"
else
echo "X5"
fi
fi
else
echo "Dice one is not equal 1."
fi
When used with [[, the < and > operators sort lexicographically using the current locale.
I see two options.
Do the comparison in arithmetic context:
if (( $dice1 == 1 ))
then
if (( $dice2 < 6 ))
or the old-fashioned way:
if [[ $dice1 -eq 1 ]]
then
if [[ $dice2 -lt 6 ]]

Sending a value on script start

trying to make a bash script that sets a value for use later in the script, depending on what I send when I run it;
i.e. ./script.sh 24
code:
### Setting values depending on input
#
# If value is between 7 and 13
if [[ $1 -le 13 || $1 -ge 7 ]]; then
#set value to
VALUE=7
# else if value is between 14 and 29
elif [[ $1 -le 29 || $1 -ge 14 ]]; then
#else set value to
VALUE=14
# else if value is larger than 30
elif [[ $1 -le 30 ]]; then
#Set value to VALUE=30
#else
# echo nope
fi
# This is just for showing what's going on
echo "input: $1"; echo "value: $VALUE"
# Do other stuff here...
But I can't get it to work properly, it only sets "value" to 7, no matter what i send on script start.
Does bash read values in any special order? When I run it with ./script.sh 24 I gives "value=7" but it (in my mind) should be "value=14"
In the first comparison:
$1 -le 13 || $1 -ge 7
You are checking if a value is below 13 or above 7. All natural numbers matches this condition, like: 1 is below 13, 100 is avobe 7, and 10 is below 13 and avobe 7. So you always enter in the first branch.
Maybe you are looking for numbers above 7 AND below 13:
$1 -le 13 && $1 -ge 7

Bash parse list for prime numbers.

I want to parse a list of hexas that i get from a file after parsing them for unique hexas and sorting them.
The list looks like this:
1 0xb6e38000
8 0xb6f66000
5 0xb6f69000
1 0xb6f6c000
3 0xb6fd4000
1 0xb6ff7000
2 0xb6ff8000
4 0xb6ffa000
1 0xb6ffb000
Now what i want to do, is refine it even more so that i get only the hexas with prime numbers in front, like this:
1 0xb6e38000
5 0xb6f69000
1 0xb6f6c000
3 0xb6fd4000
1 0xb6ff7000
1 0xb6ffb000
The command i have been using is this:
sort | uniq -c | grep " 1 0x"
But this command lists only the ones that appear only once in the file.
Can anyone help me "sort" this out ?
Original answer for prime numbers
As mentioned in the comments to the question, 1 is not prime, but in the description of desired output you list the lines starting with 1. However, if you actually want to filter out the lines with prime numbers in the first column, then the following script will help:
#!/bin/bash -
[ $# -gt 0 ] && source_file="$1"
: ${source_file:=/tmp/source-file}
function is_prime {
declare -i n="$1"
if [ $n -le 1 ]; then
return 1
elif [ $n -le 3 ]; then
return 0
elif [[ $(( $n % 2 )) == 0 || $(( $n % 3 )) == 0 ]]; then
return 1
fi
i=5
while [[ $(( $i * $i )) -le $n ]]; do
if [[ $(( $n % $i )) == 0 || $(( $n % ($i + 2) )) == 0 ]]; then
return 1
fi
(( i += 6 ))
done
return 0
}
while read -a line; do
# We accept only two or more columns
[ ${#line[#]} -ge 2 ] || continue;
if is_prime ${line[0]}; then
echo $line
else
echo >&2 "skipping ${line[*]} as ${line[0]} is not prime"
fi
done < "$source_file"
In this script we define is_prime function which returns zero (success status), if the first argument ($1) is a prime number. Non-zero means non-prime number. The algorithm is a version of this pseudo-code translated into Bash.
Then we read $source_file line by line with the while loop where we put the columns into the line array: read -a line. Then we check if is_prime ${line[0]} command exits with a success code (zero) and output the line if it is. Otherwise, we print a message to the standard error (echo >&2).
The script accepts optional argument for the source file path. It assigns $source_file to /tmp/source-file, if the first argument is missing.
Script usage
Save the above-mentioned code in script.sh file.
Call bash script.sh /path/to/source-file >filtered 2>errors
Examine the contents of filtered and errors files. The first will contain the lines filtered out from the source file.
The output files will look like the following:
filtered
5 0xb6f69000
3 0xb6fd4000
2 0xb6ff8000
errors
skipping 1 0xb6e38000 as 1 is not prime
skipping 8 0xb6f66000 as 8 is not prime
skipping 1 0xb6f6c000 as 1 is not prime
skipping 1 0xb6ff7000 as 1 is not prime
skipping 4 0xb6ffa000 as 4 is not prime
skipping 1 0xb6ffb000 as 1 is not prime
Update for odd numbers
i sould have mentioned this from the start, i need odd number lines to
show up, not prime. My bad. – biotic
It's easy to detect if a number is odd with an expression like if [[ $(( $n % 2 )) != 0 ]]. The expression checks if the remainder by 2 is not equal to zero, i.e. applies the modulo operation. If the remainder is zero, then the number is even, otherwise odd, of course.
The full script:
#!/bin/bash -
[ $# -gt 0 ] && source_file="$1"
: ${source_file:=/tmp/source-file}
while read -a line; do
# We accept only two or more columns
[ ${#line[#]} -ge 2 ] || continue;
declare -i n=${line[0]}
if [[ $(( $n % 2 )) != 0 ]]; then
echo ${line[*]}
else
echo >&2 "skipping ${line[*]} as ${line[0]} is even"
fi
done < "$source_file"
As for above, you run bash script.sh /path/to/source-file >filtered 2>errors
Sample output:
filtered
1 0xb6e38000
5 0xb6f69000
1 0xb6f6c000
3 0xb6fd4000
1 0xb6ff7000
1 0xb6ffb000
errors
skipping 8 0xb6f66000 as 8 is even
skipping 2 0xb6ff8000 as 2 is even
skipping 4 0xb6ffa000 as 4 is even

Bash script for finding the square root of a number(Babylonian Method)

I wrote this code to find the square root of a given x number, but the last part gives me "integer expression expected" , what can I do?
(I'm a shell/bash noob)
#bin/bash
2 clear
3 echo "Hello, we will calculate the square root of a number x"
4 echo "We're going to use the Babylonian Method"
5 echo "Give me a valor for x"
6 read x
7 if [ $x -lt 0 ]
8 then
9 clear
10 echo "The roots of this number are imaginary"
11 elif [ $x -eq 0 ]
12 then
13 clear
14 echo "The square root of 0 is 0"
15 else
16 echo "Now give me a base b and a vertical height h so that bh=x"
17
18 echo "Give me b"
19 read b
20 echo "Give me h"
21 read h
22 fi
23
24 if [ $b -eq $h ]
25 then
26 echo:"b or h are already the square root of h"
27 else
28 until [ $b -eq $h ]
29 do
30 b=`echo "scale=3; ($b + $h)/2"|bc -l`
31 h=`echo "scale=3; $x/$b"|bc -l`
32 done
33 fi
34 echo "the square root of x is $b or $h"
35
Bash can only handle integers. The same applies to [ ... ] (check man test):
INTEGER1 -eq INTEGER2
INTEGER1 is equal to INTEGER2
In order to compare floats, use bc as you already correctly do for the counting.

Resources