Is there any simple way to remove leading zeros from a negative number in shell?
For example : for a number like -02, the output will be -2
There a multiply ways to do this:
a="-02"
echo "$((a+0))"
Another with regex:
a="-02"
echo "${a//-0/-}"
Or
a="-02"
[[ "$a" =~ ^(-*|\+*)0*(.*)$ ]]
echo "${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
And bc:
a="-02"
bc <<< "$a + 0"
What about using the builtin printf?
$ num=-02
$ printf "%d\n" "$num"
-2
One solution as I know is the following :
echo -02 | awk '{$0=int($0)}1'
but it only works with integer number. For floating is there any way?
Related
I have this Bash script and I had a problem in line 16.
How can I take the previous result of line 15 and add
it to the variable in line 16?
#!/bin/bash
num=0
metab=0
for ((i=1; i<=2; i++)); do
for j in `ls output-$i-*`; do
echo "$j"
metab=$(cat $j|grep EndBuffer|awk '{sum+=$2} END { print sum/120}') (line15)
num= $num + $metab (line16)
done
echo "$num"
done
For integers:
Use arithmetic expansion: $((EXPR))
num=$((num1 + num2))
num=$(($num1 + $num2)) # Also works
num=$((num1 + 2 + 3)) # ...
num=$[num1+num2] # Old, deprecated arithmetic expression syntax
Using the external expr utility. Note that this is only needed for really old systems.
num=`expr $num1 + $num2` # Whitespace for expr is important
For floating point:
Bash doesn't directly support this, but there are a couple of external tools you can use:
num=$(awk "BEGIN {print $num1+$num2; exit}")
num=$(python -c "print $num1+$num2")
num=$(perl -e "print $num1+$num2")
num=$(echo $num1 + $num2 | bc) # Whitespace for echo is important
You can also use scientific notation (for example, 2.5e+2).
Common pitfalls:
When setting a variable, you cannot have whitespace on either side of =, otherwise it will force the shell to interpret the first word as the name of the application to run (for example, num= or num)
num= 1 num =2
bc and expr expect each number and operator as a separate argument, so whitespace is important. They cannot process arguments like 3+ +4.
num=`expr $num1+ $num2`
Use the $(( )) arithmetic expansion.
num=$(( $num + $metab ))
See Chapter 13. Arithmetic Expansion for more information.
There are a thousand and one ways to do it. Here's one using dc (a reverse Polish desk calculator which supports unlimited precision arithmetic):
dc <<<"$num1 $num2 + p"
But if that's too bash-y for you (or portability matters) you could say
echo $num1 $num2 + p | dc
But maybe you're one of those people who thinks RPN is icky and weird; don't worry! bc is here for you:
bc <<< "$num1 + $num2"
echo $num1 + $num2 | bc
That said, there are some unrelated improvements you could be making to your script:
#!/bin/bash
num=0
metab=0
for ((i=1; i<=2; i++)); do
for j in output-$i-* ; do # 'for' can glob directly, no need to ls
echo "$j"
# 'grep' can read files, no need to use 'cat'
metab=$(grep EndBuffer "$j" | awk '{sum+=$2} END { print sum/120}')
num=$(( $num + $metab ))
done
echo "$num"
done
As described in Bash FAQ 022, Bash does not natively support floating point numbers. If you need to sum floating point numbers the use of an external tool (like bc or dc) is required.
In this case the solution would be
num=$(dc <<<"$num $metab + p")
To add accumulate possibly-floating-point numbers into num.
In Bash,
num=5
x=6
(( num += x ))
echo $num # ==> 11
Note that Bash can only handle integer arithmetic, so if your AWK command returns a fraction, then you'll want to redesign: here's your code rewritten a bit to do all math in AWK.
num=0
for ((i=1; i<=2; i++)); do
for j in output-$i-*; do
echo "$j"
num=$(
awk -v n="$num" '
/EndBuffer/ {sum += $2}
END {print n + (sum/120)}
' "$j"
)
done
echo "$num"
done
I always forget the syntax so I come to Google Search, but then I never find the one I'm familiar with :P. This is the cleanest to me and more true to what I'd expect in other languages.
i=0
((i++))
echo $i;
I really like this method as well. There is less clutter:
count=$[count+1]
#!/bin/bash
read X
read Y
echo "$(($X+$Y))"
You should declare metab as integer and then use arithmetic evaluation
declare -i metab num
...
num+=metab
...
For more information, see 6.5 Shell Arithmetic.
Use the shell built-in let. It is similar to (( expr )):
A=1
B=1
let "C = $A + $B"
echo $C # C == 2
Source: Bash let builtin command
Another portable POSIX compliant way to do in Bash, which can be defined as a function in .bashrc for all the arithmetic operators of convenience.
addNumbers () {
local IFS='+'
printf "%s\n" "$(( $* ))"
}
and just call it in command-line as,
addNumbers 1 2 3 4 5 100
115
The idea is to use the Input-Field-Separator(IFS), a special variable in Bash used for word splitting after expansion and to split lines into words. The function changes the value locally to use word-splitting character as the sum operator +.
Remember the IFS is changed locally and does not take effect on the default IFS behaviour outside the function scope. An excerpt from the man bash page,
The shell treats each character of IFS as a delimiter, and splits the results of the other expansions into words on these characters. If IFS is unset, or its value is exactly , the default, then sequences of , , and at the beginning and end of the results of the previous expansions are ignored, and any sequence of IFS characters not at the beginning or end serves to delimit words.
The "$(( $* ))" represents the list of arguments passed to be split by + and later the sum value is output using the printf function. The function can be extended to add scope for other arithmetic operations also.
#!/usr/bin/bash
#integer numbers
#===============#
num1=30
num2=5
echo $(( num1 + num2 ))
echo $(( num1-num2 ))
echo $(( num1*num2 ))
echo $(( num1/num2 ))
echo $(( num1%num2 ))
read -p "Enter first number : " a
read -p "Enter second number : " b
# we can store the result
result=$(( a+b ))
echo sum of $a \& $b is $result # \ is used to espace &
#decimal numbers
#bash only support integers so we have to delegate to a tool such as bc
#==============#
num2=3.4
num1=534.3
echo $num1+$num2 | bc
echo $num1-$num2 | bc
echo $num1*$num2 |bc
echo "scale=20;$num1/$num2" | bc
echo $num1%$num2 | bc
# we can store the result
#result=$( ( echo $num1+$num2 ) | bc )
result=$( echo $num1+$num2 | bc )
echo result is $result
##Bonus##
#Calling built in methods of bc
num=27
echo "scale=2;sqrt($num)" | bc -l # bc provides support for calculating square root
echo "scale=2;$num^3" | bc -l # calculate power
#!/bin/bash
num=0
metab=0
for ((i=1; i<=2; i++)); do
for j in `ls output-$i-*`; do
echo "$j"
metab=$(cat $j|grep EndBuffer|awk '{sum+=$2} END { print sum/120}') (line15)
let num=num+metab (line 16)
done
echo "$num"
done
Works on MacOS. bc is a command line calculator
#!/bin/bash
sum=0
for (( i=1; i<=5; i++ )); do
sum=$(echo "$sum + 1.1" | bc) # bc: if you want to use decimal
done
echo "Total: $sum"
I want to get average of numbers with decimals. I wrote this but it's getting me the following error message when i write decimals numbers:
./average.sh: line 10: 1.2: syntax error: invalid arithmetic operator (error token is ".2")
my average operation is:
i=1;
sum=0;
while [[ i -lt 4 ]]
do
read nr
echo "scale=2; $nr" | bc -l
sum=$((sum+nr))
i=$((i+1))
done
echo "scale=2; $sum / 4" | bc -l
How can i modify it in order to accept input with decimals? Thanks.
You can use bc as you do below:
sum=$( echo $sum + $nr | bc -l )
Your code runs fine on my system (bash 4.4.20), but you should modify line 1 of your script to i=0 if you want to read 4 numbers. It would help if you also provide your input along with your script.
However, a more easier way would be using datamash by:
datamash mean 1 # Calculate the mean of the first field
Edit
If you can't use datamash, awk would be good for you:
awk '{sum += $0} END {print sum / NR}'
As dibery says, you need to modify to i=0 if you want to calculate average for 4 numbers.
For reading input with decimals,
read -p 'Enter: ' var
nr="$( printf '%.2f' "$var" )"
not very elegant but could work...
For summing, I think you can't sum directly like integers in bash, so maybe try this
sum="$( echo "$sum+$nr" | bc)"
For printing the output, this is the format I prefer to use
bc -l <<< "$sum/4"
My server doesn't accept "datamash" or "bc" utilitaries and i want to sum numbers with decimals from keyboard, not from file.
I tried some examples, but its don't work:
i=1; sum=0; while [[ i -le 3 ]]; do read nr; awk '{sum+=$nr} END { print "media:"; sum/i}'; i=$((i+1));done
I'm working on a script that compares two very similar decimal numbers and I want the script to print the portion that the two numbers share. For example, suppose I have the numbers 42.86579 and 42.84578. Since both numbers have the 42.8 portion in common, I would like the script to output 42.8. How should I go about implementing this?
You could search for the longest common prefix in two strings with sed:
$ x=42.86579
$ y=42.84578
$ sed "s/\(.*\).* \1.*/\1/" <<< "$x $y"
42.8
Or slightly more concisely using GNU grep:
$ grep -Po '(.*).* \K\1' <<< "$x $y"
42.8
a=42.86579
b=42.84578
[[ ${a%.*} != ${b%.*} ]] && exit
for ((i=0;i<${#a};i++)); do
if [[ ${a:$i:1} == ${b:$i:1} ]]; then
echo -n ${a:$i:1}
else
break
fi
done
Output:
42.8
See: Bash's parameter expansion
I'm writing a bash script to get some podcasts. The problem is that some of the podcast numbers are one digits while others are two/three digits, therefore I need to pad them to make them all 3 digits.
I tried the following:
n=1
n = printf %03d $n
wget http://aolradio.podcast.aol.com/sn/SN-$n.mp3
but the variable 'n' doesn't stay padded permanently. How can I make it permanent?
Use backticks to assign the result of the printf command (``):
n=1
wget http://aolradio.podcast.aol.com/sn/SN-`printf %03d $n`.mp3
EDIT: Note that i removed one line which was not really necessary.
If you want to assign the output of 'printf %...' to n, you could
use
n=`printf %03d $n`
and after that, use the $n variable substitution you used before.
Seems you're assigning the return value of the printf command (which is its exit code), you want to assign the output of printf.
bash-3.2$ n=1
bash-3.2$ n=$(printf %03d $n)
bash-3.2$ echo $n
001
Attention though if your input string has a leading zero!
printf will still do the padding, but also convert your string to hex octal format.
# looks ok
$ echo `printf "%05d" 03`
00003
# but not for numbers over 8
$ echo `printf "%05d" 033`
00027
A solution to this seems to be printing a float instead of decimal.
The trick is omitting the decimal places with .0f.
# works with leading zero
$ echo `printf "%05.0f" 033`
00033
# as well as without
$ echo `printf "%05.0f" 33`
00033
to avoid context switching:
a="123"
b="00000${a}"
c="${b: -5}"
n=`printf '%03d' "2"`
Note spacing and backticks
As mentioned by noselad, please command substitution, i.e. $(...), is preferable as it supercedes backtics, i.e. `...`.
Much easier to work with when trying to nest several command substitutions instead of escaping, i.e. "backslashing", backtics.
This is in response to an answer given by cC Xx.
It will work only until a's value less is than 5 digits.
Consider when a=12345678.
It'll truncate the leading digits:
a="12345678"
b="00000${a}"
c="${b: -5}"
echo "$a, $b, $c"
This gives the following output:
12345678, 0000012345678, 45678
Putting an if to check value of a is less than 5 digits and then doing it could be solution:
if [[ $a -lt 9999 ]] ; then b="00000${a}" ; c="${b: -5}" ; else c=$a; fi
Just typing this here for additional information.
If you know the number of zeroes you need, you can use the string concatenation:
let pad="0"
pad+=1
echo $pad # this will print 01
This question already has answers here:
How can I compare two floating point numbers in Bash?
(22 answers)
Closed 3 years ago.
Hey I would like to convert a string to a number
x="0.80"
#I would like to convert x to 0.80 to compare like such:
if[ $x -gt 0.70 ]; then
echo $x >> you_made_it.txt
fi
Right now I get the error integer expression expected because I am trying to compare a string.
thanks
you can use bc
$ echo "0.8 > 0.7" | bc
1
$ echo "0.8 < 0.7" | bc
0
$ echo ".08 > 0.7" | bc
0
therefore you can check for 0 or 1 in your script.
If your values are guaranteed to be in the same form and range, you can do string comparisons:
if [[ $x > 0.70 ]]
then
echo "It's true"
fi
This will fail if x is ".8" (no leading zero), for example.
However, while Bash doesn't understand decimals, its builtin printf can format them. So you could use that to normalize your values.
$ x=.8
$ x=$(printf %.2 $x)
$ echo $x
0.80
For some reason, this solution appeals to me:
if ! echo "$x $y -p" | dc | grep > /dev/null ^-; then
echo "$x > $y"
else
echo "$x < $y"
fi
You'll need to be sure that $x and $y are valid (eg
contain only numbers and zero or one '.') and,
depending on how old your dc is, you may need to
specify something like '10k' to get it to
recognize non-integer values.
Here is my simple solution:
BaseLine=70.0
if [ $string \> $BaseLine ]
then
echo $string
else
echo "TOO SMALL"
fi
use awk
x="0.80"
y="0.70"
result=$(awk -vx=$x -vy=$y 'BEGIN{ print x>=y?1:0}')
if [ "$result" -eq 1 ];then
echo "x more than y"
fi
The bash language is best characterized as a full-featured macro processor, as such there is no difference between numbers and strings. The problem is that test(1) works on integers.