expr displaying the expression rather than solving it - bash

Here is my shell script,
#! /bin/sh
# basic calculator
echo "Please input your choice"
printf " 1.Addition \n 2.SUbstraction \n 3.Multiplication \n 4.Division\n"
read choice
case "$choice" in
1) echo "Enter number 1:";read n1;echo "Enter number 2:";read n2;t=$(expr "$n1"+"$n2");echo "$n1+$n2=$t";;
2) echo "Enter number 1:";read n1;echo "Enter number 2:";read n2;t='expr $n1-$n2';echo "$n1-$n2=$t";;
3) echo "Enter number 1:";read n1;echo "Enter number 2:";read n2;t='expr $n1\*$n2';echo "$n1*$n2=$t";;
4) echo "Enter number 1:";read n1;echo "Enter number 2:";read n2;t='expr $n1/$n2';echo "$n1/$n2=$t";;
esac
Here is my output,
Script started on Sunday 08 November 2015 12:05:21 PM IST
Please input your choice
1.Addition
2.SUbstraction
3.Multiplication
4.Division
1
Enter number 1:
5
Enter number 2:
6
5+6=5+6
The problem is that my expr isnt actually solving the expressions

Whitespace matters:
$ expr 5+6
5+6
$ expr 5 + 6
11
To do arithmetic, you need to give expr 3 distinct arguments.

In some versions of expr it's recommended to use shell arithmetic instead:
$ echo $((5+6))
11
$ echo $((5>=6))
0
Using shell arithmetic using spaces to separate ints is not necessary if desired.
The expr utility makes no lexical distinction between arguments which
may
be operators and arguments which may be operands. An operand which is
lexically identical to an operator will be considered a syntax error.
The syntax of the expr command in general is historic and inconvenient.
New applications are advised to use shell arithmetic rather than expr

You are using single quotes (') instead of backticks ( ` ) for the other calls to expr. But as I'L'l points out, there's almost never any reason to use expr to perform arithmetic.

Related

Bash Error when echo print two number variables [duplicate]

echo 3+3
How can I evaluate such expressions in Bash, in this case to 6?
echo $(( 3+3 ))
expr is the standard way, but it only handles integers.
bash has a couple of extensions, which only handle integers as well:
$((3+3)) returns 6
((3+3)) used in conditionals, returns 0 for true (non-zero) and 1 for false
let 3+3 same as (( ))
let and (( )) can be used to assign values, e.g.
let a=3+3
((a=3+3))
for floating point you can use bc
echo 3+3 | bc
in shells such as zsh/ksh, you can use floats for maths. If you need more maths power, use tools like bc/awk/dc
eg
var=$(echo "scale=2;3.4+43.1" | bc)
var=$(awk 'BEGIN{print 3.4*43.1}')
looking at what you are trying to do
awk '{printf "%.2f\n",$0/59.5}' ball_dropping_times >bull_velocities
You can make use of the expr command as:
expr 3 + 3
To store the result into a variable you can do:
sum=$(expr 3 + 3)
or
sum=`expr 3 + 3`
Lots of ways - most portable is to use the expr command:
expr 3 + 3
I believe the ((3+3)) method is the most rapid as it's interpreted by the shell rather than an external binary.
time a large loop using all suggested methods for the most efficient.
Solved thanks to Dennis, an example of BC-use:
$ cat calc_velo.sh
#!/bin/bash
for i in `cat ball_dropping_times`
do
echo "scale=20; $i / 59.5" | bc
done > ball_velocities
My understanding of math processing involves floating point processing.
Using bashj (https://sourceforge.net/projects/bashj/) you can call a java method (with floating point processing, cos(), sin(), log(), exp()...) using simply
bashj +eval "3+3"
bashj +eval "3.5*5.5"
or in a bashj script, java calls of this kind:
#!/usr/bin/bashj
EXPR="3.0*6.0"
echo $EXPR "=" u.doubleEval($EXPR)
FUNCTIONX="3*x*x+cos(x)+1"
X=3.0
FX=u.doubleEval($FUNCTIONX,$X)
echo "x="$X " => f(x)=" $FUNCTIONX "=" $FX
Note the interesting speed : ~ 10 msec per call (the answer is provided by a JVM server).
Note also that u.doubleEval(1/2) will provide 0.5 (floating point) instead of 0 (integer)
One use case that might be useful in this regard is, if one of your operand itself is a bash command then try this.
echo $(( `date +%s\`+10 )) or even echo $(( `date +%s\`+(60*60) ))
In my case I was trying to get Unixtime 10 seconds and hour later than current time respectively.

Taking a count from file, I want to print no of variables using shell/bash

Taking count from file, say if count = 5, I want to print 5 variables. i.e. A B C D E.
If count = 2, Print 2 variables A B, etc.
I have tried using the ASCII values but couldn't go through it.
for i in {1..5}; do
count=5; a=0;
printf "\x$(printf %x '65+$a')";
count=count+1;
done
if count = 5, I want to print 5 variables. i.e. A B C D E. If count = 2, Print 2 variables A B, etc.
Here's a program that matches your style that does what you are looking for:
a=0
for i in {1..5}; do
printf "\x$(printf %x $(( 65 + a )) )";
a=$((a+1));
done
The first thing to note is that in order to do math in bash, you'll need to use the $(( )) operation. Above, you can see I replaced you '65+$a' with $(( 65 + a )) . That's the big news that you need to get math done.
There were a couple of other little issues, but you were stuck on the $(()) stuff so they weren't clear yet. Incidentally, the 'a' variable can be completely removed from the program to just use the 'i' variable like this:
for i in {1..5}; do
printf "\x$(printf %x $(( 64 + i )) )";
done
I had to change the constant to 64 since we are now counting starting at 1.
The {1..5} expression is a good short cut for 1 2 3 4 5, but you won't be able to put a variable into it. So, if you need to add a count variable back in, consider using the seq program instead like this:
count=$1
for i in $(seq 1 $count); do
printf "\x$(printf %x $(( 64 + i )) )";
done
Note that $() is different than the math operator $(()). $() runs a subcommand returning the results.
method 1: simple brace expansion
#!/bin/bash
# generate a lookup table
vars=( - $(echo {A..Z}) )
# use the elements
for i in {1..5}; do
echo ${vars[$i]}
done
{A..Z} generates 26 strings: A, B, ..., Z
which get stored in an array variable by vars=(...)
we prepend a - that we'll ignore
we can then do 1-based indexing into the array
limited to 26 variables (or whatever range we choose)
method 2: multiple brace expansion to generate arbitrary long variables
#!/bin/bash
if [[ ! $1 =~ ^[0-9]+$ ]]; then
echo "Usage: $0 count"
exit
fi
cmd='{A..Z}'
for (( i=$1; i>26; i=i/26 )); do
cmd="${A..Z}$cmd"
done
vars=( $(eval echo $cmd) )
for (( i=0; i<$1; i++ )); do
echo ${vars[$i]}
done
i/26 does integer division (throws away the remainder)
I'm lazy and generate "more than enough" variables rather than attempting to calculate how many is "exactly enough"
{a..b}{a..b}{a..b} becomes aaa aab aba abb baa bab bba bbb
using eval lets us do the brace expansion without knowing in advance how many sets are needed
Sample output:
$ mkvar.sh 10000 |fmt -64 | tail -5
ORY ORZ OSA OSB OSC OSD OSE OSF OSG OSH OSI OSJ OSK OSL OSM
OSN OSO OSP OSQ OSR OSS OST OSU OSV OSW OSX OSY OSZ OTA OTB
OTC OTD OTE OTF OTG OTH OTI OTJ OTK OTL OTM OTN OTO OTP OTQ
OTR OTS OTT OTU OTV OTW OTX OTY OTZ OUA OUB OUC OUD OUE OUF
OUG OUH OUI OUJ OUK OUL OUM OUN OUO OUP

Value too great for base (error token is "09")

When running this part of my bash script am getting an error
Script
value=0
for (( t=0; t <= 4; t++ ))
do
d1=${filedates[$t]}
d2=${filedates[$t+1]}
((diff_sec=d2-d1))
SEC=$diff_sec
compare=$((${SEC}/(60*60*24)))
value=$((value+compare))
done
Output
jad.sh: line 28: ((: 10#2014-01-09: value too great for base (error token is "09")
jad.sh: line 30: /(60*60*24): syntax error: operand expected (error token is "/(60*60*24)")
d1 and d2 are dates in that form 2014-01-09 and 2014-01-10
Any solution please?
Prepend the string "10#" to the front of your variables. That forces bash to treat them as decimal, even though the leading zero would normally make them octal.
What are d1 and d2? Are they dates or seconds?
Generally, this error occurs if you are trying to do arithmetic with numbers containing a zero-prefix e.g. 09.
Example:
$ echo $((09+1))
-bash: 09: value too great for base (error token is "09")
In order to perform arithmetic with 0-prefixed numbers you need to tell bash to use base-10 by specifying 10#:
$ echo $((10#09+1))
10
As others have said, the error results from Bash interpreting digit sequences with leading zeros as octal numbers. If you have control over the process creating the date values and you're using date, you can prefix the output format string with a hyphen to remove leading zero padding.
Without prefixing date format with hyphen:
$ (( $(date --date='9:00' +%H) > 10 )) && echo true || echo oops
-bash: ((: 09: value too great for base (error token is "09")
oops
With prefixing date format with hyphen:
$ (( $(date --date='9:00' +%-H) > 10 )) && echo true || echo oops
true
From the date man page:
By default, date pads numeric fields with zeroes. The following
optional flags may follow '%':
- (hyphen) do not pad the field
d1 and d2 are dates in that form 2014-01-09 and 2014-01-10
and then
((diff_sec=d2-d1))
What do you expect to get? ((diffsec=2014-01-09-2014-01-10)) ??
You need to convert the dates to seconds first:
d1=$( date -d "${filedates[$t]}" +%s )
d2=$( date -d "${filedates[$t+1]}" +%s )
(( compare = (d2 - d1) / (60*60*24) ))
(( value += compare ))
Posting some tips here related to the title of this question, but not directly related to the details of the original question. I realize that's a bit controversial action on Stack Overflow, however these related questions:
convert octal to decimal in bash [duplicate]
Value too great for base (error token is "08") [duplicate]
point to this one, and yet they are closed and hence, I could not post this answer there. Therefore, this seemed like a logical place (at least to me) to post this information that may help others in a similar situation, especially new-to-BaSH programmers.
An alternative approach to ensuring a number is treated as a 10-base integer is to use printf. This command instructs printf to treat $num as an integer and round it to 0 decimal places.
num="$(printf "%.0f" "$num")"
Or, if you want to also ensure there are no non-numeric characters in the string, you can do this:
num="$(printf "%.0f" "${num//[!0-9]/}")"
Both commands will strip out leading zeroes and round decimal values to the nearest whole number. Note the first (simpler) solution works with negative numbers, but the second does not (it will always return absolute value).
Note that printf rounds down, meaning .01 to 0.5 is rounded down to 0, while .51 to .99 is rounded up to 1. Basically, the difference between rounding up versus down in this case is that printf rounds down 0.5 and any below. I mention this because 0.5 rounded up is a more common practice.
Now, addressing the OP's specific scenario.... Combining printf with awk allows arithmetic expressions not possible with printf alone.
This
compare=$((${SEC}/(606024)))
could be alternatively be expressed as
compare=$(awk -v sec=$SEC 'BEGIN { print int(sec/(60*60*24))}')
or
compare="$(printf "%.0f" "$(awk "BEGIN { print ( $SEC / ( 60 * 60 * 24 ) ) }")")"
Meanwhile,
value=$((value+compare))
Could be calculated as
value="$(printf "%.0f" "$(awk "BEGIN { print ( $value + $compare ) }")")"
You don't need the $ and the {} in an arithmetic expansion expression. It should look like this:
compare=$((SEC/(60*60*24)))
For 'mm' and 'dd' values in dates, I use this trick:
mm="1${date:5,2}" # where 5 is the offset to mm in the date
let mm=$mm-100 # turn 108 into 8, and 109 into 9

Convert string into integer in bash script - "Leading Zero" number error

In a text file, test.txt, I have the next information:
sl-gs5 desconnected Wed Oct 10 08:00:01 EDT 2012 1001
I want to extract the hour of the event by the next command line:
hour=$(grep -n sl-gs5 test.txt | tail -1 | cut -d' ' -f6 | awk -F ":" '{print $1}')
and I got "08". When I try to add 1,
14 echo $((hour+1))
I receive the next error message:
./test2.sh: line 14: 08: value too great for base (error token is "08")
If variables in Bash are untyped, why?
See ARITHMETIC EVALUATION in man bash:
Constants with a leading 0 are interpreted as octal numbers.
You can remove the leading zero by parameter expansion:
hour=${hour#0}
or force base-10 interpretation:
$((10#$hour + 1))
what I'd call a hack, but given that you're only processing hour values, you can do
hour=08
echo $(( ${hour#0} +1 ))
9
hour=10
echo $(( ${hour#0} +1))
11
with little risk.
IHTH.
You could also use bc
hour=8
result=$(echo "$hour + 1" | bc)
echo $result
9
Here's an easy way, albeit not the prettiest way to get an int value for a string.
hour=`expr $hour + 0`
Example
bash-3.2$ hour="08"
bash-3.2$ hour=`expr $hour + 0`
bash-3.2$ echo $hour
8
In Short: In order to deal with "Leading Zero" numbers (any 0 digit that comes before the first non-zero) in bash
- Use bc An arbitrary precision calculator language
Example:
a="000001"
b=$(echo $a | bc)
echo $b
Output: 1
From Bash manual:
"bc is a language that supports arbitrary precision numbers with interactive execution
of statements. There are some similarities in the syntax to the C programming lan-
guage. A standard math library is available by command line option. If requested, the
math library is defined before processing any files. bc starts by processing code from
all the files listed on the command line in the order listed. After all files have
been processed, bc reads from the standard input. All code is executed as it is read.
(If a file contains a command to halt the processor, bc will never read from the standard input.)"
Since hours are always positive, and always 2 digits, you can set a 1 in front of it and subtract 100:
echo $((1$hour+1-100))
which is equivalent to
echo $((1$hour-99))
Be sure to comment such gymnastics. :)
The leading 0 is leading to bash trying to interpret your number as an octal number, but octal numbers are 0-7, and 8 is thus an invalid token.
If I were you, I would add some logic to remove a leading 0, add one, and re-add the leading 0 if the result is < 10.
How about sed?
hour=`echo $hour|sed -e "s/^0*//g"`

How to use mod operator in bash?

I'm trying a line like this:
for i in {1..600}; do wget http://example.com/search/link $i % 5; done;
What I'm trying to get as output is:
wget http://example.com/search/link0
wget http://example.com/search/link1
wget http://example.com/search/link2
wget http://example.com/search/link3
wget http://example.com/search/link4
wget http://example.com/search/link0
But what I'm actually getting is just:
wget http://example.com/search/link
Try the following:
for i in {1..600}; do echo wget http://example.com/search/link$(($i % 5)); done
The $(( )) syntax does an arithmetic evaluation of the contents.
for i in {1..600}
do
n=$(($i%5))
wget http://example.com/search/link$n
done
You must put your mathematical expressions inside $(( )).
One-liner:
for i in {1..600}; do wget http://example.com/search/link$(($i % 5)); done;
Multiple lines:
for i in {1..600}; do
wget http://example.com/search/link$(($i % 5))
done
This might be off-topic. But for the wget in for loop, you can certainly do
curl -O http://example.com/search/link[1-600]
Math in bash: how to use all bash operators, and arithmetic expansion, in bash
Of the 346k visitors to this question thus far, I'd be willing to bet 344.9k of them just want the title of this question answered 😃:
How to use mod operator in bash?
Even I googled "bash modulus" looking for that answer, and landed here. So, now that I've figured it out, let's just jump straight to it:
How to use the modulus (%) operator in bash
Just do this, for instance:
# 7 mod 4 (answer is 3, but to print the output you must use one of the cmds
# below)
$((7 % 4))
# [PREFERRED: no quotes]
# print the result (double quotes are not required)
echo $((7 % 4))
# print the result (with double quotes if you like)
echo "$((7 % 4))"
Example with variables:
num1="7"
num2="4"
# [PREFERRED: no $ signs nor extraneous quotes] result is 3
echo $((num1 % num2))
# Also ok: with $ signs
echo $(($num1 % $num2))
# Also ok: with $ signs and extra quotes
echo "$(("$num1" % "$num2"))"
Store the result into a variable:
mod=$((num1 % num2))
echo "$mod" # result is 3
The main links to study for these concepts are these, from the official GNU bash user manual:
Bash Arithmetic Expansion
Bash Shell Arithmetic
More on bash "arithmetic expansion"
I learned the above from #Mark Longair's answer (although it took me some effort to comprehend it all), and that's where I got the link just below. I then did more research.
The $(( )) part is called "Arithmetic Expansion", and is described in the official GNU bash user manual here: https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Arithmetic-Expansion.
Basic examples (place echo in front of each one to see the result print to the screen):
# general form
$((mathematical_expression))
# addition
$((7 + 4)) # 11
# subtraction
$((7 - 4)) # 3
# modulus (remainder)
$((7 % 4)) # 3
# logical AND
$((7 && 4)) # 1
# bitwise AND
$((7 & 4)) # 4
# etc.
# See the full operator list below for more
Double quotes around the arithmetic expansion are not needed. From the manual above (emphasis added):
The expression is treated as if it were within double quotes, but a double quote inside the parentheses is not treated specially. All tokens in the expression undergo parameter and variable expansion, command substitution, and quote removal. The result is treated as the arithmetic expression to be evaluated. Arithmetic expansions may be nested.
For all shell arithmetic operators, see the "Shell Arithmetic" section of the GNU bash manual here: https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Arithmetic
You essentially have all of the C language mathematical operators at your disposal. The arithmetic is done "in fixed-width integers with no check for overflow", so if you do echo $((11/10)) or echo $((19/10)) you'll get 1 in both cases since the fractional part is truncated for integers.
From the manual link just above (emphasis added):
Evaluation is done in fixed-width integers with no check for overflow, though division by 0 is trapped and flagged as an error. The operators and their precedence, associativity, and values are the same as in the C language.
Since the arithmetic operators in bash have the same precedence as in C, as it states above, you can also reference the C Operator Precedence from the cppreference community wiki here: https://en.cppreference.com/w/c/language/operator_precedence <-- put that in your toolbag.
Shell Arithmetic: here are all of the supported operators from the GNU Bash manual
They are listed in order of highest to lowest precedence:
id++ id--
variable post-increment and post-decrement
++id --id
variable pre-increment and pre-decrement
- +
unary minus and plus
! ~
logical and bitwise negation
**
exponentiation
* / %
multiplication, division, remainder
+ -
addition, subtraction
<< >>
left and right bitwise shifts
<= >= < >
comparison
== !=
equality and inequality
&
bitwise AND
^
bitwise exclusive OR
|
bitwise OR
&&
logical AND
||
logical OR
expr ? expr : expr
conditional operator
= *= /= %= += -= <<= >>= &= ^= |=
assignment
expr1 , expr2
comma
Using alternate bases in your arithmetic, such as binary (base-2), octal (base-8), and hex (base-16)
To learn about using different bases, such as base-2 (binary), base-8 (octal) or base-16 (hex) with the bash arithmetic operators, read the next couple paragraphs below the "Shell Arithmetic" list above in the manual.
Here are a few quick examples with input numbers which are decimal (base-10), octal (base-8), hex (base-16), and binary (base-2), used in the math:
# hex 0xa (decimal 10) + decimal 5 = decimal 15
echo $((0xa + 5)) # prints `15` (decimal 15)
# OR (same thing)
echo $((16#a + 5)) # prints `15` (decimal 15)
# octal 071 (decimal 57) + hex 0xaa (decimal 170) = decimal 227
echo $((071 + 0xaa)) # prints `227` (decimal 227)
# OR (same thing)
echo $((8#71 + 16#aa)) # prints `227` (decimal 227)
# binary 1011 (decimal 11) + decimal 2 = decimal 13
echo $((2#1011 + 2)) # prints `13` (decimal 13)
# binary 1111 (decimal 15) + binary 11111 (decimal 31) = decimal 46
echo $((2#1111 + 2#11111)) # prints `46` (decimal 46)
To print as hex, use printf "0x%X\n" number:
# prints `0x2E` (hex 2E, or decimal 46)
printf "0x%X\n" $((2#1111 + 2#11111))
To print as binary, use bc (see my answer here):
# prints `0b101110` (decimal 46)
printf "0b%s\n" "$(echo "obase=2; $((2#1111 + 2#11111))" | bc)"
This post is rather old but I thought I would contribute since I stumbled upon it while trying to research the same issue of setting keyboard color through automation.
I created a simple BASH script that I call from my ROOT chrontab every minute to set the keyboard color as the day progresses. You can tweak the color patterns and the modulo to match your needs. This is just a good starting point.
#!/bin/bash
# must run as ROOT to work
# put in your root crontab to change the color at set times
sec=$(date +%s)
min=$(( $sec / 60 ))
col=$(( $min % 7 ))
colors=('0000FF' '00FF00' '00FFFF' 'FF0000' 'FF00FF' 'FFFF00' 'FFFFFF')
colorFile="/sys/class/leds/system76_acpi::kbd_backlight/color"
if [ -f "$colorFile" ]; then
echo "Set keyboard to color $col ~ ${colors[$col]}"
echo "${colors[$col]}" > "$colorFile"
fi
Hope you like it.

Resources