For reference, I'm using this version of the shell.
I'm looking to evaluate a math expression containing exponents. How can I do so? expr isn't available in es-shell, and neither do the double parends work (as they do in other shells).
The expression I want to evaluate is 2^69 (2 to the 69th power). I've tried with both ** and ^ for exponentiation.
I'm looking for a solution that doesn't use an external calculator, hopefully pure es-shell code.
Most Unix shells delegate math to some other command. bc is probably available on your machine, since it's a POSIX utility. Invoke it from es like this:
; echo `{echo '2 ^ 69' | bc}
590295810358705651712
Related
So I have two files in my directory that contain a number in each of them. I want to make a script that calculates the average of these two numbers. How would I write it? Would this be correct?
avg=$((${<file1.txt}-${<file2.txt})/2)
Your example does not work. Furthermore, your formula is probably incorrect. Here are two options without unnecessary cat:
avg=$(( (`<file1.txt` + `<file2.txt`) / 2 ))
or
avg=$(( ($(<file1.txt) + $(<file2.txt)) / 2 ))
I find the first one more readable though. Also be warned: this trivial approach will cause problems when your files contain more than just the plain numbers.
EDIT:
I should have noted that the first syntactical/legacy option which uses the backticks (` `) is no longer recommended and should be avoided. You can read more about the WHY here. Thanks at mklement0 for the link!
EDIT2:
According to Eric, the values are floating point numbers. You can't do this directly in bash because only integer numbers are supported. You have to use a little helper:
avg=$(bc <<< "( $(<file1.txt) + $(<file2.txt) ) / 2")
or maybe easier to understand
avg=$(echo "( $(<file1.txt) + $(<file2.txt) ) / 2" | bc)
For those who might wonder what bc is (see man bc):
bc is a language that supports arbitrary precision numbers with
interactive execution of statements.
Here is another alternative since perl is usually installed by default:
avg=$(perl -e 'print( ($ARGV[0] + $ARGV[1]) / 2 )' -- $(<file1.txt) $(<file2.txt))
You'll want to use a command substitution:
avg=$(($(cat file1.txt)-$(cat file2.txt)/2))
However, Bash is a pretty bad language for doing maths (at least unless it's completely integer maths). You might want to look into bc or a "real" language like Python.
This question already has answers here:
Does "untyped" also mean "dynamically typed" in the academic CS world?
(9 answers)
Closed 6 years ago.
According to Advanced Bash-Scripting Guide,
bash variables are untyped:
Unlike many other programming languages, Bash does not segregate its variables by "type." Essentially, Bash variables are character
strings, but, depending on context, Bash permits arithmetic
operations and comparisons on variables. The determining factor is
whether the value of a variable contains only digits.
The link also gives examples.
Does "untyped" mean the same as the concept of "dynamically typing"
in programming languages? If not, what are the relations and
differences between the two?
To lighten the burden of keeping track of variable types in a script, Bash does permit declaring variables.
For example, declare a variable to be integer type, by declare -i
myvariable.
Is this called "typed" variables? Does "typed" mean
the same as the concept of "statically typing"?
Most of this has been well answered here...
Does "untyped" also mean "dynamically typed" in the academic CS world?
by at least two people that are very familiar with the matter. To most of us that have not studied type systems etc to that level 'untyped' means dynamic typing but it's a misnomer in academic circles, see post above. untyped actually means there are no types ie think assembly, Bash is typed, it figures out it's types at runtime. Lets take the following sentence from the Advanced Bash Scripting Guide, emphasis mine...
http://tldp.org/LDP/abs/html/untyped.html
Unlike many other programming languages, Bash does not segregate its
variables by "type." Essentially, Bash variables are character
strings, but, depending on context, Bash permits arithmetic operations
and comparisons on variables. The determining factor is whether the
value of a variable contains only digits.
Bash figures out that something is a number at runtime ie it's dynamically typed.
In assembler on a 64bit machine I can store any 8 bytes in a register and decrement it, it doesn't check to see if the things were chars etc, there is no context about the thing it's about to decrement it just decrements the 64 bits, it doesn't check or work out anything about the type of the thing it's decrementing.
Perl is not an untyped language but the following code might make it seem like it treats everything as integers ie
#!/usr/bin/perl
use strict;
use warnings;
my $foo = "1";
my $bar = $foo + 1;
print("$bar\n");
$foo was assigned a string but was incremented? Does this means Perl is untyped because based on context it does what you want it to do? I don't think so.
This differs from Python, Python will actually give you the following error if you try the same thing...
Traceback (most recent call last):
File "py.py", line 2, in <module>
bar = foo + 1
If Python is dynamically typed and Perl is dynamically typed why do we see different behavior. Is it because their type systems differ or their type conversion semantics differ. In assembly do we have type conversion instructions that change a string to an integer or vice versa?
Bash has different type conversion rules
#!/bin/bash
set -e
MYVAR=WTF
let "MYVAR+=1"
echo "MYVAR == $MYVAR";
This will assign 1 to MYVAR instead of incrementing it ie if you increment a string bash sets the string to integer zero then does the increment. It's performing type conversion which means it's typed.
For anyone still believing that Bash is untyped try this....
#!/bin/bash
declare -i var1=1
var1=2367.1
You should get something like this...
foo.sh: line 3: 2367.1: syntax error: invalid arithmetic operator (error token is ".1")
But the following shows no such error
#!/bin/bash
var1=2367.1
The output of the following
#!/bin/bash
var1=2367.1
echo "$var1"
let "var1+=1"
echo "$var1"
is the same warning without declaring a type...
2367.1
foo.sh: line 4: let: 2367.1: syntax error: invalid arithmetic operator (error token is ".1")
2367.1
A much better example is this
#!/bin/bash
arg1=1234
arg2=abc
if [ $arg1 -eq $arg2 ]; then
echo "wtf";
fi
Why do I get this...
foo.sh: line 5: [: abc: integer expression expected
Bash is asking me for an integer expression.
Bash is a dynamically typed or more correctly it's a dynamically checked language. I've already added a long answer, this is the short one.
#!/bin/bash
arg1=1234
arg2=abc
if [ $arg1 -eq $arg2 ]; then
echo "wtf";
fi
gives this error message....
foo.sh: line 5: [: abc: integer expression expected
The fact I have an error that tells me I have in some way made a mistake with regards type means something is checking types.
I am working on Mac OSX and using bash as my shell. I currently have a string which I wish want evaluated as a number. When I echo the string I get 1.e8*1.07**100. Is there any way to pass this string on to be evaluated as a number?
The background as to why it is a string to start with is because the expression was built step by step. First 1.e8*1.07**%%d is within the code, then the user inputs an integer to be taken as what 1.07 will be raised to the power of. So in the example above, the user would have input 100, and thus the script is stuck with 1.e8*1.07**100, which is the correct expression I was hoping for, but I would have liked it to be evaluated when I echo the variable where it is store.
Actual important bits of code:
BASE=$(printf '1.e8*1.07**%%d')
#Get user input assigned to pow
NUM=$(printf ${BASE} ${pow})
echo $NUM #1.e8*1.07**100
Thanks for any help you can offer.
[Edit: I would also like to not just echo the answer, but store it as a variable.]
How about:
python -c "print $NUM"
By the way, you could just write
BASE="1.e8*1.07**%d"
(In fact, you don't even need the quotes.)
In most unix* systems you'll find a tool called bc that can perform calculations. You'll might need to rewrite your input though, I thinks it accepts ^ instead of **, and I'm not sure about the 1.e8 notation.
It happens that perl can evaluate that exact expression
$ x="1.e8*1.07**100"
$ y=$(perl -E "say $x")
$ echo $y
86771632556.6417
Looking for an easy way to make a random number, i came across a web page that used the follow snip of code
echo $[ ( $RANDOM % $DIE_SIDES ) + 1 ]
What is the purpose of the $[. Googling did not reveal the answer I seek. Thanks
The $[expression] construct is used for arithmetic; $[1+1], for example, returns 2. You can also say $((expression)) or expr 1 + 1. The expr command version is old-school and should work in any shell, the $[expression] and $((expression)) versions work in bash but I'm not sure if they're covered by POSIX.
Update: The $[expression] form is a bash extension, the $((expression)) form is specified for the POSIX shell.
I have just stumbled upon the bash syntax:
foo=42
bar=$[foo+1] # evaluates an arithmetic expression
When I Googled for this, I found http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html#sect_03_04_05:
3.4.6. Arithmetic expansion
Arithmetic expansion allows the evaluation of an arithmetic expression and the substitution of the result. The format for arithmetic expansion is:
$(( EXPRESSION ))
...
Wherever possible, Bash users should try to use the syntax with square brackets:
$[ EXPRESSION ]
However, this will only calculate the result of EXPRESSION, and do no tests...
In my bash man page I can only find the $(( EXPRESSION )) form such as:
foo=42
bar=$((foo+1)) # evaluates an arithmetic expression
So what tests are not performed with $[...] that do with $((...)), or is the $[...] just a legacy version of $((...))?
The manpage for bash v3.2.48 says:
[...] The format for arithmetic expansion is:
$((expression))
The old format $[expression] is deprecated and will be removed in upcoming versions
of bash.
So $[...] is old syntax that should not be used anymore.
#sth is entirely correct. And in case you are curious about why a more verbose syntax is now in favor, check out this old email from the mailing list.
http://lists.gnu.org/archive/html/bug-bash/2012-04/msg00033.html
“In early proposals, a form $[expression] was used. It was functionally
equivalent to the "$(())" of the current text, but objections were
lodged that the 1988 KornShell had already implemented "$(())" and
there was no compelling reason to invent yet another syntax.
Furthermore, the "$[]" syntax had a minor incompatibility involving
the patterns in case statements.”
I am not sure that I like the rationale “but someone has already done this more verbosely,” but there you have it—maybe the case-statement problem was more compelling than I am imagining from this obscure mention?