Do math operation on the numbers typed into command line without call bc
up vote
6
down vote
favorite
Sometimes I need to run some math operations. I know I can use bc or echo $(( 6/2 )). I have created my own function for bc to read input. But sometimes it takes a long time to type: _bc "6/2". So I have this question:
Is there way to teach zsh/bash how to run math operation for numbers in command line? One example is more than thousands words.
$ 6/2
$ 3.0
It means that zsh/bash must recognize numbers and call i.e. bc.
bash command-line zsh arithmetic
|
show 2 more comments
up vote
6
down vote
favorite
Sometimes I need to run some math operations. I know I can use bc or echo $(( 6/2 )). I have created my own function for bc to read input. But sometimes it takes a long time to type: _bc "6/2". So I have this question:
Is there way to teach zsh/bash how to run math operation for numbers in command line? One example is more than thousands words.
$ 6/2
$ 3.0
It means that zsh/bash must recognize numbers and call i.e. bc.
bash command-line zsh arithmetic
4
This reminds me of this question (which isn’t a duplicate).
– Stephen Kitt
yesterday
1
While I've never seen a script called, for example,2+2, it could conceivably exist, in which case you'd never be able to run it.
– Jeff Schaller
yesterday
@JeffSchaller Or a bit more plausibly maybe2/3. You could still run it by typing something like2+2, if2+2was parsed as an alias forecho $((2+2)).
– Gilles
yesterday
1
Wouldn't c 1+2+3 be a good alternative? (Calculate and then you can put anything you want, no spaces needed, same commands as bc if you want)
– Fabby
yesterday
in powershell 6/2 and simply works
– phuclv
yesterday
|
show 2 more comments
up vote
6
down vote
favorite
up vote
6
down vote
favorite
Sometimes I need to run some math operations. I know I can use bc or echo $(( 6/2 )). I have created my own function for bc to read input. But sometimes it takes a long time to type: _bc "6/2". So I have this question:
Is there way to teach zsh/bash how to run math operation for numbers in command line? One example is more than thousands words.
$ 6/2
$ 3.0
It means that zsh/bash must recognize numbers and call i.e. bc.
bash command-line zsh arithmetic
Sometimes I need to run some math operations. I know I can use bc or echo $(( 6/2 )). I have created my own function for bc to read input. But sometimes it takes a long time to type: _bc "6/2". So I have this question:
Is there way to teach zsh/bash how to run math operation for numbers in command line? One example is more than thousands words.
$ 6/2
$ 3.0
It means that zsh/bash must recognize numbers and call i.e. bc.
bash command-line zsh arithmetic
bash command-line zsh arithmetic
edited 20 hours ago
Isaac
10.6k11447
10.6k11447
asked yesterday
waldauf
9517
9517
4
This reminds me of this question (which isn’t a duplicate).
– Stephen Kitt
yesterday
1
While I've never seen a script called, for example,2+2, it could conceivably exist, in which case you'd never be able to run it.
– Jeff Schaller
yesterday
@JeffSchaller Or a bit more plausibly maybe2/3. You could still run it by typing something like2+2, if2+2was parsed as an alias forecho $((2+2)).
– Gilles
yesterday
1
Wouldn't c 1+2+3 be a good alternative? (Calculate and then you can put anything you want, no spaces needed, same commands as bc if you want)
– Fabby
yesterday
in powershell 6/2 and simply works
– phuclv
yesterday
|
show 2 more comments
4
This reminds me of this question (which isn’t a duplicate).
– Stephen Kitt
yesterday
1
While I've never seen a script called, for example,2+2, it could conceivably exist, in which case you'd never be able to run it.
– Jeff Schaller
yesterday
@JeffSchaller Or a bit more plausibly maybe2/3. You could still run it by typing something like2+2, if2+2was parsed as an alias forecho $((2+2)).
– Gilles
yesterday
1
Wouldn't c 1+2+3 be a good alternative? (Calculate and then you can put anything you want, no spaces needed, same commands as bc if you want)
– Fabby
yesterday
in powershell 6/2 and simply works
– phuclv
yesterday
4
4
This reminds me of this question (which isn’t a duplicate).
– Stephen Kitt
yesterday
This reminds me of this question (which isn’t a duplicate).
– Stephen Kitt
yesterday
1
1
While I've never seen a script called, for example,
2+2, it could conceivably exist, in which case you'd never be able to run it.– Jeff Schaller
yesterday
While I've never seen a script called, for example,
2+2, it could conceivably exist, in which case you'd never be able to run it.– Jeff Schaller
yesterday
@JeffSchaller Or a bit more plausibly maybe
2/3. You could still run it by typing something like 2+2, if 2+2 was parsed as an alias for echo $((2+2)).– Gilles
yesterday
@JeffSchaller Or a bit more plausibly maybe
2/3. You could still run it by typing something like 2+2, if 2+2 was parsed as an alias for echo $((2+2)).– Gilles
yesterday
1
1
Wouldn't c 1+2+3 be a good alternative? (Calculate and then you can put anything you want, no spaces needed, same commands as bc if you want)
– Fabby
yesterday
Wouldn't c 1+2+3 be a good alternative? (Calculate and then you can put anything you want, no spaces needed, same commands as bc if you want)
– Fabby
yesterday
in powershell 6/2 and simply works
– phuclv
yesterday
in powershell 6/2 and simply works
– phuclv
yesterday
|
show 2 more comments
6 Answers
6
active
oldest
votes
up vote
15
down vote
accepted
Shortcut Alt-c (bash)
With bash, using the readline utility, we can define a key sequence to place the word calc at the start and enclose the text written so far into double quotes:
bind '"ec": "C-acalc "e[F""'
Having executed that, you type 23 + 46 * 89 for example, then Alt-c to get:
calc "23 + 46 * 89"
Just press enter and the math will be executed by the function defined as calc, which could be as simple as, or a lot more complex:
calc () { <<<"$*" bc -l; }
a (+) Alias
We can define an alias:
alias +='calc #'
Which will comment the whole command line typed so far. You type:
+ (56 * 23 + 26) / 17
When you press enter, the line will be converted to calc #(56 * 23 + 26) / 17 and the command calc will be called. If calc is this function:
bash
calc(){ s=$(HISTTIMEFORMAT='' history 1); # recover last command line.
s=${s#*[ ]}; # remove initial spaces.
s=${s#*[0-9]}; # remove history line number.
s=${s#*[ ]+}; # remove more spaces.
eval 'bc -l <<<"'"$s"'"'; # calculate the line.
}
ksh
calc(){ s=$(history -1 | # last command(s)
sed '$!d;s/^[ t]*[0-9]*[ t]*+ //'); # clean it up
# (assume one line commads)
eval 'bc -l <<<"'"$s"'"'; # Do the math.
}
zsh zsh doesn't allow neither a + alias nor a # character.
The value will be printed as:
$ + (56 * 23 + 26) / 17
77.29411764705882352941
Only a + is required, String is quoted (no globs), shell variables accepted:
$ a=23
$ + (56 * 23 + $a) / 17
77.11764705882352941176
a (+) Function
With some limitations, this is the closest I got to your request with a function (in bash):
+() { bc -l <<< "$*"; }
Which will work like this:
$ + 25+68+8/24
93.33333333333333333333
The problem is that the shell parsing isn't avoided and a * (for example) could get expanded to the list of files in the pwd.
If you write the command line without (white) spaces you will probably be ok.
Beware of writing things like $(...) because they will get expanded.
The safe solution is to quote the string to be evaluated:
$ + '45 + (58+3 * l(23))/7'
54.62949752111249272462
$ + '4 * a(1) * 2'
6.28318530717958647688
Which is only two characters shorter that your _bc "6/2", but a + seems more intuitive to me.
@Issac Thank you for your answer. Yes, it's shorter - but it's about name of function which you choose. In my own name convention has every function underscore at the beginning of name.
– waldauf
15 hours ago
@waldauf Added a readline solution (no+needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
– Isaac
15 hours ago
@waldauf The name of the function could perfectly be named_calcor anything else.
– Isaac
15 hours ago
@Isaac Solution with keyboard shortcut looks great! I'll try to do it forbindkeyin ZSH. :]
– waldauf
13 hours ago
@waldauf It seems a bit strange to both pick a convention that has an underscore at the beginning of all names (may require the shift key!) and complain about_bctaking a long time to type :-)
– ShreevatsaR
7 hours ago
add a comment |
up vote
10
down vote
I use a variant of bash's magic alias hack:
asis() { bc <<< "$(history 1 | perl -pe 's/^ *[0-9]+ +[^ ]+ //')"; }
alias c='asis #'
Then:
$ c 1+1
2
$ c -10 + 20 / 5
-6
$ c (-10 + 20) / 5
2
$ c 2^8 / 13
19
$ c scale=5; 2^8 / 13
19.69230
The magic is the fact that alias expansion happens before the usual command line processing, which allows us to create a command whose remaining arguments follow a comment character, that the implementing function finds with the history command.
This magic allows me to type *, (, and other characters literally. But that also means I can't use shell variables because $ is also literal:
$ x=5.0
$ y=-1.2
$ z=4.7
$ c ($x + $y) > $z
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $
I get around this by a bit of bootstrapping:
$ echo "x=$x; y=$y; z=$z"
x=5.0; y=-1.2; z=4.7
$ c x=5.0; y=-1.2; z=4.7; (x + y) > z
0
You might just be better off typing: bc Enter 1 + 1 Enter Control+D
As a side note, I have my default bc settings (like scale) in $HOME/.bc and I use bc -l in the alias. Your use may not require these modifications.
why are you saying that you can't use shell variables? you can!alias c='calc #'; calc(){ eval "$(history -a /dev/stdout | sed 's/c /expr /;s/[()*/+-]/ \& /g')"; };thenx=33; c ($x/3)*10. This has a great potential, thanks for the trick!
– mosvy
yesterday
@mosvy Well, can't as written in my answer. But certainlyevalraises this hack to all-new heights!
– bishop
yesterday
1
+1 Thanks for your idea, I implemented an alternative in my answer. Note that the value ofHISTTIMEFORMATneeds to be cleared to avoid the time tag printed byhistory. Perl seems like too much for this little edition use.
– Isaac
14 hours ago
add a comment |
up vote
8
down vote
In zsh, you could do something like:
autoload zcalc
accept-line() {
if [[ $BUFFER =~ '^[ (]*[+-]? *(0[xX]|.)?[[:digit:]]+[^[:alnum:]]' ]]; then
echo
zcalc -e $BUFFER
print -rs -- $BUFFER
BUFFER=
fi
zle .$WIDGET
}
zle -N accept-line
It redefines the accept-line widget (mapped on Enter) to a user-defined widget that checks if the current line starts with a number (decimal or hexadecimal) optionally prefixed with any number of (s, looking for a non-alnum character after that to avoid false positives for commands like 7zip or 411toppm.
If that matches then we pass it to zcalc (more useful than bc in that it can use shell variables and all of zsh math functions and number styles, but does not support arbitrary precision), add the line to history and accept an empty command.
Note that it can cause confusion if you enter a line with digits in things like:
cat << EOF
213 whatever
EOF
Or:
var=(
123 456
)
add a comment |
up vote
6
down vote
Inspired by Stéphane's zsh answer, and longer than Isaac's bash answer in total but shorter in operation:
trap '[[ $_ =~ [[:digit:]] ]] && bc -l <<< "$_"' ERR
This also has the side effect of showing a "No such file or directory" error each time:
$ foozle
-bash: foozle: command not found
$ 1+2+3
-bash: 1+2+3: command not found
6
$ 6/3
-bash: 6/3: No such file or directory
2.00000000000000000000
The regex could be tightened, depending on the operations you expect to perform.
This (ab)uses the bash behavior of calling the ERR trap when a given command does not exist. If the last command (in $_) contains a digit, then it executes bc on that "command".
Thanks to a hint from Stéphane, here's a slightly cleaner way to achieve the result (requires bash 4.0 or later, which introduced the functionality):
if ! declare -F command_not_found_handle > /dev/null
then
command_not_found_handle() {
if [[ "$@" =~ [[:digit:]] ]]; then
bc <<< "$@";
else
printf 'bash: %s: command not foundn' "$1" >&2
return 127
fi
}
else
echo Unable to set up the handler function, sorry
fi
The function is called any time a command isn't found. If that command contains a digit, we throw it through bc; otherwise, we emit a message similar to bash's stock message and return a 127 exit code.
Caveat - the ERR trap is not inherited to subshells, so(1+2)would need to be'(1+2)'
– Jeff Schaller
yesterday
2
bashandzshhave acommand-not-found-handlerhook (mispelledhandleinbash) that you could also use here.
– Stéphane Chazelas
yesterday
Thanks, Stéphane! I've incorporated the idea.
– Jeff Schaller
yesterday
2
command_not_found_handledoesn't work on something like123/456, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With theERRtrap, I think you could also use$BASH_COMMANDto get the full command line, and not just the last word. That would make expressions with spaces work.
– ilkkachu
yesterday
1
A workaround using readline is possible and seems simpler/easier.
– Isaac
14 hours ago
|
show 2 more comments
up vote
3
down vote
The following command lines are rather simple to type,
<<< 5+4 bc
<<< 6/3 bc
<<< 7*2 bc
and slightly more complicated with parentheses (must be quoted or escaped),
<<< "(5+4)*2/3" bc
<<< (5+4)*2/3 bc
add a comment |
up vote
1
down vote
Another imperfect way of doing that in Bash would be to use the DEBUG trap, which runs on every command. With extdebug set, the trap handler can prevent the main command from running, so you don't get the "command not found" errors.
$ cat bash_calc.sh
shopt -s extdebug
debug_calc() {
local re='^[ (]*-?[0-9]'
if [[ $BASH_COMMAND =~ $re ]]; then
echo "$BASH_COMMAND" | bc -l
return 1
fi
}
trap debug_calc DEBUG
$ . ./bash_calc.sh
$ 123 * 456
56088
$ 123/456
.26973684210526315789
The trap gets the full command line before expanding variables or filename patterns, so an unquoted * works. (But using shell variables in the calculation doesn't work.)
However, unquoted parenthesis still cause a syntax error, so this is not perfect either.
(I nicked the regex above from Stéphane's answer.)
Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
– Isaac
14 hours ago
@Isaac, hmm, that's not good. I'll admit I'd never tested it for speed, though I wonder if it matters here if this is for interactive use. Anyway, as mentioned, this is just another imperfect idea. I originally wasn't going to post it at all, but there are other somewhat hackish Bash solutions here, too, so I think it can stay.
– ilkkachu
9 hours ago
add a comment |
6 Answers
6
active
oldest
votes
6 Answers
6
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
15
down vote
accepted
Shortcut Alt-c (bash)
With bash, using the readline utility, we can define a key sequence to place the word calc at the start and enclose the text written so far into double quotes:
bind '"ec": "C-acalc "e[F""'
Having executed that, you type 23 + 46 * 89 for example, then Alt-c to get:
calc "23 + 46 * 89"
Just press enter and the math will be executed by the function defined as calc, which could be as simple as, or a lot more complex:
calc () { <<<"$*" bc -l; }
a (+) Alias
We can define an alias:
alias +='calc #'
Which will comment the whole command line typed so far. You type:
+ (56 * 23 + 26) / 17
When you press enter, the line will be converted to calc #(56 * 23 + 26) / 17 and the command calc will be called. If calc is this function:
bash
calc(){ s=$(HISTTIMEFORMAT='' history 1); # recover last command line.
s=${s#*[ ]}; # remove initial spaces.
s=${s#*[0-9]}; # remove history line number.
s=${s#*[ ]+}; # remove more spaces.
eval 'bc -l <<<"'"$s"'"'; # calculate the line.
}
ksh
calc(){ s=$(history -1 | # last command(s)
sed '$!d;s/^[ t]*[0-9]*[ t]*+ //'); # clean it up
# (assume one line commads)
eval 'bc -l <<<"'"$s"'"'; # Do the math.
}
zsh zsh doesn't allow neither a + alias nor a # character.
The value will be printed as:
$ + (56 * 23 + 26) / 17
77.29411764705882352941
Only a + is required, String is quoted (no globs), shell variables accepted:
$ a=23
$ + (56 * 23 + $a) / 17
77.11764705882352941176
a (+) Function
With some limitations, this is the closest I got to your request with a function (in bash):
+() { bc -l <<< "$*"; }
Which will work like this:
$ + 25+68+8/24
93.33333333333333333333
The problem is that the shell parsing isn't avoided and a * (for example) could get expanded to the list of files in the pwd.
If you write the command line without (white) spaces you will probably be ok.
Beware of writing things like $(...) because they will get expanded.
The safe solution is to quote the string to be evaluated:
$ + '45 + (58+3 * l(23))/7'
54.62949752111249272462
$ + '4 * a(1) * 2'
6.28318530717958647688
Which is only two characters shorter that your _bc "6/2", but a + seems more intuitive to me.
@Issac Thank you for your answer. Yes, it's shorter - but it's about name of function which you choose. In my own name convention has every function underscore at the beginning of name.
– waldauf
15 hours ago
@waldauf Added a readline solution (no+needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
– Isaac
15 hours ago
@waldauf The name of the function could perfectly be named_calcor anything else.
– Isaac
15 hours ago
@Isaac Solution with keyboard shortcut looks great! I'll try to do it forbindkeyin ZSH. :]
– waldauf
13 hours ago
@waldauf It seems a bit strange to both pick a convention that has an underscore at the beginning of all names (may require the shift key!) and complain about_bctaking a long time to type :-)
– ShreevatsaR
7 hours ago
add a comment |
up vote
15
down vote
accepted
Shortcut Alt-c (bash)
With bash, using the readline utility, we can define a key sequence to place the word calc at the start and enclose the text written so far into double quotes:
bind '"ec": "C-acalc "e[F""'
Having executed that, you type 23 + 46 * 89 for example, then Alt-c to get:
calc "23 + 46 * 89"
Just press enter and the math will be executed by the function defined as calc, which could be as simple as, or a lot more complex:
calc () { <<<"$*" bc -l; }
a (+) Alias
We can define an alias:
alias +='calc #'
Which will comment the whole command line typed so far. You type:
+ (56 * 23 + 26) / 17
When you press enter, the line will be converted to calc #(56 * 23 + 26) / 17 and the command calc will be called. If calc is this function:
bash
calc(){ s=$(HISTTIMEFORMAT='' history 1); # recover last command line.
s=${s#*[ ]}; # remove initial spaces.
s=${s#*[0-9]}; # remove history line number.
s=${s#*[ ]+}; # remove more spaces.
eval 'bc -l <<<"'"$s"'"'; # calculate the line.
}
ksh
calc(){ s=$(history -1 | # last command(s)
sed '$!d;s/^[ t]*[0-9]*[ t]*+ //'); # clean it up
# (assume one line commads)
eval 'bc -l <<<"'"$s"'"'; # Do the math.
}
zsh zsh doesn't allow neither a + alias nor a # character.
The value will be printed as:
$ + (56 * 23 + 26) / 17
77.29411764705882352941
Only a + is required, String is quoted (no globs), shell variables accepted:
$ a=23
$ + (56 * 23 + $a) / 17
77.11764705882352941176
a (+) Function
With some limitations, this is the closest I got to your request with a function (in bash):
+() { bc -l <<< "$*"; }
Which will work like this:
$ + 25+68+8/24
93.33333333333333333333
The problem is that the shell parsing isn't avoided and a * (for example) could get expanded to the list of files in the pwd.
If you write the command line without (white) spaces you will probably be ok.
Beware of writing things like $(...) because they will get expanded.
The safe solution is to quote the string to be evaluated:
$ + '45 + (58+3 * l(23))/7'
54.62949752111249272462
$ + '4 * a(1) * 2'
6.28318530717958647688
Which is only two characters shorter that your _bc "6/2", but a + seems more intuitive to me.
@Issac Thank you for your answer. Yes, it's shorter - but it's about name of function which you choose. In my own name convention has every function underscore at the beginning of name.
– waldauf
15 hours ago
@waldauf Added a readline solution (no+needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
– Isaac
15 hours ago
@waldauf The name of the function could perfectly be named_calcor anything else.
– Isaac
15 hours ago
@Isaac Solution with keyboard shortcut looks great! I'll try to do it forbindkeyin ZSH. :]
– waldauf
13 hours ago
@waldauf It seems a bit strange to both pick a convention that has an underscore at the beginning of all names (may require the shift key!) and complain about_bctaking a long time to type :-)
– ShreevatsaR
7 hours ago
add a comment |
up vote
15
down vote
accepted
up vote
15
down vote
accepted
Shortcut Alt-c (bash)
With bash, using the readline utility, we can define a key sequence to place the word calc at the start and enclose the text written so far into double quotes:
bind '"ec": "C-acalc "e[F""'
Having executed that, you type 23 + 46 * 89 for example, then Alt-c to get:
calc "23 + 46 * 89"
Just press enter and the math will be executed by the function defined as calc, which could be as simple as, or a lot more complex:
calc () { <<<"$*" bc -l; }
a (+) Alias
We can define an alias:
alias +='calc #'
Which will comment the whole command line typed so far. You type:
+ (56 * 23 + 26) / 17
When you press enter, the line will be converted to calc #(56 * 23 + 26) / 17 and the command calc will be called. If calc is this function:
bash
calc(){ s=$(HISTTIMEFORMAT='' history 1); # recover last command line.
s=${s#*[ ]}; # remove initial spaces.
s=${s#*[0-9]}; # remove history line number.
s=${s#*[ ]+}; # remove more spaces.
eval 'bc -l <<<"'"$s"'"'; # calculate the line.
}
ksh
calc(){ s=$(history -1 | # last command(s)
sed '$!d;s/^[ t]*[0-9]*[ t]*+ //'); # clean it up
# (assume one line commads)
eval 'bc -l <<<"'"$s"'"'; # Do the math.
}
zsh zsh doesn't allow neither a + alias nor a # character.
The value will be printed as:
$ + (56 * 23 + 26) / 17
77.29411764705882352941
Only a + is required, String is quoted (no globs), shell variables accepted:
$ a=23
$ + (56 * 23 + $a) / 17
77.11764705882352941176
a (+) Function
With some limitations, this is the closest I got to your request with a function (in bash):
+() { bc -l <<< "$*"; }
Which will work like this:
$ + 25+68+8/24
93.33333333333333333333
The problem is that the shell parsing isn't avoided and a * (for example) could get expanded to the list of files in the pwd.
If you write the command line without (white) spaces you will probably be ok.
Beware of writing things like $(...) because they will get expanded.
The safe solution is to quote the string to be evaluated:
$ + '45 + (58+3 * l(23))/7'
54.62949752111249272462
$ + '4 * a(1) * 2'
6.28318530717958647688
Which is only two characters shorter that your _bc "6/2", but a + seems more intuitive to me.
Shortcut Alt-c (bash)
With bash, using the readline utility, we can define a key sequence to place the word calc at the start and enclose the text written so far into double quotes:
bind '"ec": "C-acalc "e[F""'
Having executed that, you type 23 + 46 * 89 for example, then Alt-c to get:
calc "23 + 46 * 89"
Just press enter and the math will be executed by the function defined as calc, which could be as simple as, or a lot more complex:
calc () { <<<"$*" bc -l; }
a (+) Alias
We can define an alias:
alias +='calc #'
Which will comment the whole command line typed so far. You type:
+ (56 * 23 + 26) / 17
When you press enter, the line will be converted to calc #(56 * 23 + 26) / 17 and the command calc will be called. If calc is this function:
bash
calc(){ s=$(HISTTIMEFORMAT='' history 1); # recover last command line.
s=${s#*[ ]}; # remove initial spaces.
s=${s#*[0-9]}; # remove history line number.
s=${s#*[ ]+}; # remove more spaces.
eval 'bc -l <<<"'"$s"'"'; # calculate the line.
}
ksh
calc(){ s=$(history -1 | # last command(s)
sed '$!d;s/^[ t]*[0-9]*[ t]*+ //'); # clean it up
# (assume one line commads)
eval 'bc -l <<<"'"$s"'"'; # Do the math.
}
zsh zsh doesn't allow neither a + alias nor a # character.
The value will be printed as:
$ + (56 * 23 + 26) / 17
77.29411764705882352941
Only a + is required, String is quoted (no globs), shell variables accepted:
$ a=23
$ + (56 * 23 + $a) / 17
77.11764705882352941176
a (+) Function
With some limitations, this is the closest I got to your request with a function (in bash):
+() { bc -l <<< "$*"; }
Which will work like this:
$ + 25+68+8/24
93.33333333333333333333
The problem is that the shell parsing isn't avoided and a * (for example) could get expanded to the list of files in the pwd.
If you write the command line without (white) spaces you will probably be ok.
Beware of writing things like $(...) because they will get expanded.
The safe solution is to quote the string to be evaluated:
$ + '45 + (58+3 * l(23))/7'
54.62949752111249272462
$ + '4 * a(1) * 2'
6.28318530717958647688
Which is only two characters shorter that your _bc "6/2", but a + seems more intuitive to me.
edited 14 hours ago
answered yesterday
Isaac
10.6k11447
10.6k11447
@Issac Thank you for your answer. Yes, it's shorter - but it's about name of function which you choose. In my own name convention has every function underscore at the beginning of name.
– waldauf
15 hours ago
@waldauf Added a readline solution (no+needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
– Isaac
15 hours ago
@waldauf The name of the function could perfectly be named_calcor anything else.
– Isaac
15 hours ago
@Isaac Solution with keyboard shortcut looks great! I'll try to do it forbindkeyin ZSH. :]
– waldauf
13 hours ago
@waldauf It seems a bit strange to both pick a convention that has an underscore at the beginning of all names (may require the shift key!) and complain about_bctaking a long time to type :-)
– ShreevatsaR
7 hours ago
add a comment |
@Issac Thank you for your answer. Yes, it's shorter - but it's about name of function which you choose. In my own name convention has every function underscore at the beginning of name.
– waldauf
15 hours ago
@waldauf Added a readline solution (no+needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
– Isaac
15 hours ago
@waldauf The name of the function could perfectly be named_calcor anything else.
– Isaac
15 hours ago
@Isaac Solution with keyboard shortcut looks great! I'll try to do it forbindkeyin ZSH. :]
– waldauf
13 hours ago
@waldauf It seems a bit strange to both pick a convention that has an underscore at the beginning of all names (may require the shift key!) and complain about_bctaking a long time to type :-)
– ShreevatsaR
7 hours ago
@Issac Thank you for your answer. Yes, it's shorter - but it's about name of function which you choose. In my own name convention has every function underscore at the beginning of name.
– waldauf
15 hours ago
@Issac Thank you for your answer. Yes, it's shorter - but it's about name of function which you choose. In my own name convention has every function underscore at the beginning of name.
– waldauf
15 hours ago
@waldauf Added a readline solution (no
+ needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.– Isaac
15 hours ago
@waldauf Added a readline solution (no
+ needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.– Isaac
15 hours ago
@waldauf The name of the function could perfectly be named
_calc or anything else.– Isaac
15 hours ago
@waldauf The name of the function could perfectly be named
_calc or anything else.– Isaac
15 hours ago
@Isaac Solution with keyboard shortcut looks great! I'll try to do it for
bindkey in ZSH. :]– waldauf
13 hours ago
@Isaac Solution with keyboard shortcut looks great! I'll try to do it for
bindkey in ZSH. :]– waldauf
13 hours ago
@waldauf It seems a bit strange to both pick a convention that has an underscore at the beginning of all names (may require the shift key!) and complain about
_bc taking a long time to type :-)– ShreevatsaR
7 hours ago
@waldauf It seems a bit strange to both pick a convention that has an underscore at the beginning of all names (may require the shift key!) and complain about
_bc taking a long time to type :-)– ShreevatsaR
7 hours ago
add a comment |
up vote
10
down vote
I use a variant of bash's magic alias hack:
asis() { bc <<< "$(history 1 | perl -pe 's/^ *[0-9]+ +[^ ]+ //')"; }
alias c='asis #'
Then:
$ c 1+1
2
$ c -10 + 20 / 5
-6
$ c (-10 + 20) / 5
2
$ c 2^8 / 13
19
$ c scale=5; 2^8 / 13
19.69230
The magic is the fact that alias expansion happens before the usual command line processing, which allows us to create a command whose remaining arguments follow a comment character, that the implementing function finds with the history command.
This magic allows me to type *, (, and other characters literally. But that also means I can't use shell variables because $ is also literal:
$ x=5.0
$ y=-1.2
$ z=4.7
$ c ($x + $y) > $z
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $
I get around this by a bit of bootstrapping:
$ echo "x=$x; y=$y; z=$z"
x=5.0; y=-1.2; z=4.7
$ c x=5.0; y=-1.2; z=4.7; (x + y) > z
0
You might just be better off typing: bc Enter 1 + 1 Enter Control+D
As a side note, I have my default bc settings (like scale) in $HOME/.bc and I use bc -l in the alias. Your use may not require these modifications.
why are you saying that you can't use shell variables? you can!alias c='calc #'; calc(){ eval "$(history -a /dev/stdout | sed 's/c /expr /;s/[()*/+-]/ \& /g')"; };thenx=33; c ($x/3)*10. This has a great potential, thanks for the trick!
– mosvy
yesterday
@mosvy Well, can't as written in my answer. But certainlyevalraises this hack to all-new heights!
– bishop
yesterday
1
+1 Thanks for your idea, I implemented an alternative in my answer. Note that the value ofHISTTIMEFORMATneeds to be cleared to avoid the time tag printed byhistory. Perl seems like too much for this little edition use.
– Isaac
14 hours ago
add a comment |
up vote
10
down vote
I use a variant of bash's magic alias hack:
asis() { bc <<< "$(history 1 | perl -pe 's/^ *[0-9]+ +[^ ]+ //')"; }
alias c='asis #'
Then:
$ c 1+1
2
$ c -10 + 20 / 5
-6
$ c (-10 + 20) / 5
2
$ c 2^8 / 13
19
$ c scale=5; 2^8 / 13
19.69230
The magic is the fact that alias expansion happens before the usual command line processing, which allows us to create a command whose remaining arguments follow a comment character, that the implementing function finds with the history command.
This magic allows me to type *, (, and other characters literally. But that also means I can't use shell variables because $ is also literal:
$ x=5.0
$ y=-1.2
$ z=4.7
$ c ($x + $y) > $z
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $
I get around this by a bit of bootstrapping:
$ echo "x=$x; y=$y; z=$z"
x=5.0; y=-1.2; z=4.7
$ c x=5.0; y=-1.2; z=4.7; (x + y) > z
0
You might just be better off typing: bc Enter 1 + 1 Enter Control+D
As a side note, I have my default bc settings (like scale) in $HOME/.bc and I use bc -l in the alias. Your use may not require these modifications.
why are you saying that you can't use shell variables? you can!alias c='calc #'; calc(){ eval "$(history -a /dev/stdout | sed 's/c /expr /;s/[()*/+-]/ \& /g')"; };thenx=33; c ($x/3)*10. This has a great potential, thanks for the trick!
– mosvy
yesterday
@mosvy Well, can't as written in my answer. But certainlyevalraises this hack to all-new heights!
– bishop
yesterday
1
+1 Thanks for your idea, I implemented an alternative in my answer. Note that the value ofHISTTIMEFORMATneeds to be cleared to avoid the time tag printed byhistory. Perl seems like too much for this little edition use.
– Isaac
14 hours ago
add a comment |
up vote
10
down vote
up vote
10
down vote
I use a variant of bash's magic alias hack:
asis() { bc <<< "$(history 1 | perl -pe 's/^ *[0-9]+ +[^ ]+ //')"; }
alias c='asis #'
Then:
$ c 1+1
2
$ c -10 + 20 / 5
-6
$ c (-10 + 20) / 5
2
$ c 2^8 / 13
19
$ c scale=5; 2^8 / 13
19.69230
The magic is the fact that alias expansion happens before the usual command line processing, which allows us to create a command whose remaining arguments follow a comment character, that the implementing function finds with the history command.
This magic allows me to type *, (, and other characters literally. But that also means I can't use shell variables because $ is also literal:
$ x=5.0
$ y=-1.2
$ z=4.7
$ c ($x + $y) > $z
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $
I get around this by a bit of bootstrapping:
$ echo "x=$x; y=$y; z=$z"
x=5.0; y=-1.2; z=4.7
$ c x=5.0; y=-1.2; z=4.7; (x + y) > z
0
You might just be better off typing: bc Enter 1 + 1 Enter Control+D
As a side note, I have my default bc settings (like scale) in $HOME/.bc and I use bc -l in the alias. Your use may not require these modifications.
I use a variant of bash's magic alias hack:
asis() { bc <<< "$(history 1 | perl -pe 's/^ *[0-9]+ +[^ ]+ //')"; }
alias c='asis #'
Then:
$ c 1+1
2
$ c -10 + 20 / 5
-6
$ c (-10 + 20) / 5
2
$ c 2^8 / 13
19
$ c scale=5; 2^8 / 13
19.69230
The magic is the fact that alias expansion happens before the usual command line processing, which allows us to create a command whose remaining arguments follow a comment character, that the implementing function finds with the history command.
This magic allows me to type *, (, and other characters literally. But that also means I can't use shell variables because $ is also literal:
$ x=5.0
$ y=-1.2
$ z=4.7
$ c ($x + $y) > $z
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $
I get around this by a bit of bootstrapping:
$ echo "x=$x; y=$y; z=$z"
x=5.0; y=-1.2; z=4.7
$ c x=5.0; y=-1.2; z=4.7; (x + y) > z
0
You might just be better off typing: bc Enter 1 + 1 Enter Control+D
As a side note, I have my default bc settings (like scale) in $HOME/.bc and I use bc -l in the alias. Your use may not require these modifications.
edited yesterday
answered yesterday
bishop
2,0162821
2,0162821
why are you saying that you can't use shell variables? you can!alias c='calc #'; calc(){ eval "$(history -a /dev/stdout | sed 's/c /expr /;s/[()*/+-]/ \& /g')"; };thenx=33; c ($x/3)*10. This has a great potential, thanks for the trick!
– mosvy
yesterday
@mosvy Well, can't as written in my answer. But certainlyevalraises this hack to all-new heights!
– bishop
yesterday
1
+1 Thanks for your idea, I implemented an alternative in my answer. Note that the value ofHISTTIMEFORMATneeds to be cleared to avoid the time tag printed byhistory. Perl seems like too much for this little edition use.
– Isaac
14 hours ago
add a comment |
why are you saying that you can't use shell variables? you can!alias c='calc #'; calc(){ eval "$(history -a /dev/stdout | sed 's/c /expr /;s/[()*/+-]/ \& /g')"; };thenx=33; c ($x/3)*10. This has a great potential, thanks for the trick!
– mosvy
yesterday
@mosvy Well, can't as written in my answer. But certainlyevalraises this hack to all-new heights!
– bishop
yesterday
1
+1 Thanks for your idea, I implemented an alternative in my answer. Note that the value ofHISTTIMEFORMATneeds to be cleared to avoid the time tag printed byhistory. Perl seems like too much for this little edition use.
– Isaac
14 hours ago
why are you saying that you can't use shell variables? you can!
alias c='calc #'; calc(){ eval "$(history -a /dev/stdout | sed 's/c /expr /;s/[()*/+-]/ \& /g')"; }; then x=33; c ($x/3)*10. This has a great potential, thanks for the trick!– mosvy
yesterday
why are you saying that you can't use shell variables? you can!
alias c='calc #'; calc(){ eval "$(history -a /dev/stdout | sed 's/c /expr /;s/[()*/+-]/ \& /g')"; }; then x=33; c ($x/3)*10. This has a great potential, thanks for the trick!– mosvy
yesterday
@mosvy Well, can't as written in my answer. But certainly
eval raises this hack to all-new heights!– bishop
yesterday
@mosvy Well, can't as written in my answer. But certainly
eval raises this hack to all-new heights!– bishop
yesterday
1
1
+1 Thanks for your idea, I implemented an alternative in my answer. Note that the value of
HISTTIMEFORMAT needs to be cleared to avoid the time tag printed by history. Perl seems like too much for this little edition use.– Isaac
14 hours ago
+1 Thanks for your idea, I implemented an alternative in my answer. Note that the value of
HISTTIMEFORMAT needs to be cleared to avoid the time tag printed by history. Perl seems like too much for this little edition use.– Isaac
14 hours ago
add a comment |
up vote
8
down vote
In zsh, you could do something like:
autoload zcalc
accept-line() {
if [[ $BUFFER =~ '^[ (]*[+-]? *(0[xX]|.)?[[:digit:]]+[^[:alnum:]]' ]]; then
echo
zcalc -e $BUFFER
print -rs -- $BUFFER
BUFFER=
fi
zle .$WIDGET
}
zle -N accept-line
It redefines the accept-line widget (mapped on Enter) to a user-defined widget that checks if the current line starts with a number (decimal or hexadecimal) optionally prefixed with any number of (s, looking for a non-alnum character after that to avoid false positives for commands like 7zip or 411toppm.
If that matches then we pass it to zcalc (more useful than bc in that it can use shell variables and all of zsh math functions and number styles, but does not support arbitrary precision), add the line to history and accept an empty command.
Note that it can cause confusion if you enter a line with digits in things like:
cat << EOF
213 whatever
EOF
Or:
var=(
123 456
)
add a comment |
up vote
8
down vote
In zsh, you could do something like:
autoload zcalc
accept-line() {
if [[ $BUFFER =~ '^[ (]*[+-]? *(0[xX]|.)?[[:digit:]]+[^[:alnum:]]' ]]; then
echo
zcalc -e $BUFFER
print -rs -- $BUFFER
BUFFER=
fi
zle .$WIDGET
}
zle -N accept-line
It redefines the accept-line widget (mapped on Enter) to a user-defined widget that checks if the current line starts with a number (decimal or hexadecimal) optionally prefixed with any number of (s, looking for a non-alnum character after that to avoid false positives for commands like 7zip or 411toppm.
If that matches then we pass it to zcalc (more useful than bc in that it can use shell variables and all of zsh math functions and number styles, but does not support arbitrary precision), add the line to history and accept an empty command.
Note that it can cause confusion if you enter a line with digits in things like:
cat << EOF
213 whatever
EOF
Or:
var=(
123 456
)
add a comment |
up vote
8
down vote
up vote
8
down vote
In zsh, you could do something like:
autoload zcalc
accept-line() {
if [[ $BUFFER =~ '^[ (]*[+-]? *(0[xX]|.)?[[:digit:]]+[^[:alnum:]]' ]]; then
echo
zcalc -e $BUFFER
print -rs -- $BUFFER
BUFFER=
fi
zle .$WIDGET
}
zle -N accept-line
It redefines the accept-line widget (mapped on Enter) to a user-defined widget that checks if the current line starts with a number (decimal or hexadecimal) optionally prefixed with any number of (s, looking for a non-alnum character after that to avoid false positives for commands like 7zip or 411toppm.
If that matches then we pass it to zcalc (more useful than bc in that it can use shell variables and all of zsh math functions and number styles, but does not support arbitrary precision), add the line to history and accept an empty command.
Note that it can cause confusion if you enter a line with digits in things like:
cat << EOF
213 whatever
EOF
Or:
var=(
123 456
)
In zsh, you could do something like:
autoload zcalc
accept-line() {
if [[ $BUFFER =~ '^[ (]*[+-]? *(0[xX]|.)?[[:digit:]]+[^[:alnum:]]' ]]; then
echo
zcalc -e $BUFFER
print -rs -- $BUFFER
BUFFER=
fi
zle .$WIDGET
}
zle -N accept-line
It redefines the accept-line widget (mapped on Enter) to a user-defined widget that checks if the current line starts with a number (decimal or hexadecimal) optionally prefixed with any number of (s, looking for a non-alnum character after that to avoid false positives for commands like 7zip or 411toppm.
If that matches then we pass it to zcalc (more useful than bc in that it can use shell variables and all of zsh math functions and number styles, but does not support arbitrary precision), add the line to history and accept an empty command.
Note that it can cause confusion if you enter a line with digits in things like:
cat << EOF
213 whatever
EOF
Or:
var=(
123 456
)
edited yesterday
answered yesterday
Stéphane Chazelas
296k54559904
296k54559904
add a comment |
add a comment |
up vote
6
down vote
Inspired by Stéphane's zsh answer, and longer than Isaac's bash answer in total but shorter in operation:
trap '[[ $_ =~ [[:digit:]] ]] && bc -l <<< "$_"' ERR
This also has the side effect of showing a "No such file or directory" error each time:
$ foozle
-bash: foozle: command not found
$ 1+2+3
-bash: 1+2+3: command not found
6
$ 6/3
-bash: 6/3: No such file or directory
2.00000000000000000000
The regex could be tightened, depending on the operations you expect to perform.
This (ab)uses the bash behavior of calling the ERR trap when a given command does not exist. If the last command (in $_) contains a digit, then it executes bc on that "command".
Thanks to a hint from Stéphane, here's a slightly cleaner way to achieve the result (requires bash 4.0 or later, which introduced the functionality):
if ! declare -F command_not_found_handle > /dev/null
then
command_not_found_handle() {
if [[ "$@" =~ [[:digit:]] ]]; then
bc <<< "$@";
else
printf 'bash: %s: command not foundn' "$1" >&2
return 127
fi
}
else
echo Unable to set up the handler function, sorry
fi
The function is called any time a command isn't found. If that command contains a digit, we throw it through bc; otherwise, we emit a message similar to bash's stock message and return a 127 exit code.
Caveat - the ERR trap is not inherited to subshells, so(1+2)would need to be'(1+2)'
– Jeff Schaller
yesterday
2
bashandzshhave acommand-not-found-handlerhook (mispelledhandleinbash) that you could also use here.
– Stéphane Chazelas
yesterday
Thanks, Stéphane! I've incorporated the idea.
– Jeff Schaller
yesterday
2
command_not_found_handledoesn't work on something like123/456, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With theERRtrap, I think you could also use$BASH_COMMANDto get the full command line, and not just the last word. That would make expressions with spaces work.
– ilkkachu
yesterday
1
A workaround using readline is possible and seems simpler/easier.
– Isaac
14 hours ago
|
show 2 more comments
up vote
6
down vote
Inspired by Stéphane's zsh answer, and longer than Isaac's bash answer in total but shorter in operation:
trap '[[ $_ =~ [[:digit:]] ]] && bc -l <<< "$_"' ERR
This also has the side effect of showing a "No such file or directory" error each time:
$ foozle
-bash: foozle: command not found
$ 1+2+3
-bash: 1+2+3: command not found
6
$ 6/3
-bash: 6/3: No such file or directory
2.00000000000000000000
The regex could be tightened, depending on the operations you expect to perform.
This (ab)uses the bash behavior of calling the ERR trap when a given command does not exist. If the last command (in $_) contains a digit, then it executes bc on that "command".
Thanks to a hint from Stéphane, here's a slightly cleaner way to achieve the result (requires bash 4.0 or later, which introduced the functionality):
if ! declare -F command_not_found_handle > /dev/null
then
command_not_found_handle() {
if [[ "$@" =~ [[:digit:]] ]]; then
bc <<< "$@";
else
printf 'bash: %s: command not foundn' "$1" >&2
return 127
fi
}
else
echo Unable to set up the handler function, sorry
fi
The function is called any time a command isn't found. If that command contains a digit, we throw it through bc; otherwise, we emit a message similar to bash's stock message and return a 127 exit code.
Caveat - the ERR trap is not inherited to subshells, so(1+2)would need to be'(1+2)'
– Jeff Schaller
yesterday
2
bashandzshhave acommand-not-found-handlerhook (mispelledhandleinbash) that you could also use here.
– Stéphane Chazelas
yesterday
Thanks, Stéphane! I've incorporated the idea.
– Jeff Schaller
yesterday
2
command_not_found_handledoesn't work on something like123/456, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With theERRtrap, I think you could also use$BASH_COMMANDto get the full command line, and not just the last word. That would make expressions with spaces work.
– ilkkachu
yesterday
1
A workaround using readline is possible and seems simpler/easier.
– Isaac
14 hours ago
|
show 2 more comments
up vote
6
down vote
up vote
6
down vote
Inspired by Stéphane's zsh answer, and longer than Isaac's bash answer in total but shorter in operation:
trap '[[ $_ =~ [[:digit:]] ]] && bc -l <<< "$_"' ERR
This also has the side effect of showing a "No such file or directory" error each time:
$ foozle
-bash: foozle: command not found
$ 1+2+3
-bash: 1+2+3: command not found
6
$ 6/3
-bash: 6/3: No such file or directory
2.00000000000000000000
The regex could be tightened, depending on the operations you expect to perform.
This (ab)uses the bash behavior of calling the ERR trap when a given command does not exist. If the last command (in $_) contains a digit, then it executes bc on that "command".
Thanks to a hint from Stéphane, here's a slightly cleaner way to achieve the result (requires bash 4.0 or later, which introduced the functionality):
if ! declare -F command_not_found_handle > /dev/null
then
command_not_found_handle() {
if [[ "$@" =~ [[:digit:]] ]]; then
bc <<< "$@";
else
printf 'bash: %s: command not foundn' "$1" >&2
return 127
fi
}
else
echo Unable to set up the handler function, sorry
fi
The function is called any time a command isn't found. If that command contains a digit, we throw it through bc; otherwise, we emit a message similar to bash's stock message and return a 127 exit code.
Inspired by Stéphane's zsh answer, and longer than Isaac's bash answer in total but shorter in operation:
trap '[[ $_ =~ [[:digit:]] ]] && bc -l <<< "$_"' ERR
This also has the side effect of showing a "No such file or directory" error each time:
$ foozle
-bash: foozle: command not found
$ 1+2+3
-bash: 1+2+3: command not found
6
$ 6/3
-bash: 6/3: No such file or directory
2.00000000000000000000
The regex could be tightened, depending on the operations you expect to perform.
This (ab)uses the bash behavior of calling the ERR trap when a given command does not exist. If the last command (in $_) contains a digit, then it executes bc on that "command".
Thanks to a hint from Stéphane, here's a slightly cleaner way to achieve the result (requires bash 4.0 or later, which introduced the functionality):
if ! declare -F command_not_found_handle > /dev/null
then
command_not_found_handle() {
if [[ "$@" =~ [[:digit:]] ]]; then
bc <<< "$@";
else
printf 'bash: %s: command not foundn' "$1" >&2
return 127
fi
}
else
echo Unable to set up the handler function, sorry
fi
The function is called any time a command isn't found. If that command contains a digit, we throw it through bc; otherwise, we emit a message similar to bash's stock message and return a 127 exit code.
edited yesterday
answered yesterday
Jeff Schaller
37.3k1052121
37.3k1052121
Caveat - the ERR trap is not inherited to subshells, so(1+2)would need to be'(1+2)'
– Jeff Schaller
yesterday
2
bashandzshhave acommand-not-found-handlerhook (mispelledhandleinbash) that you could also use here.
– Stéphane Chazelas
yesterday
Thanks, Stéphane! I've incorporated the idea.
– Jeff Schaller
yesterday
2
command_not_found_handledoesn't work on something like123/456, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With theERRtrap, I think you could also use$BASH_COMMANDto get the full command line, and not just the last word. That would make expressions with spaces work.
– ilkkachu
yesterday
1
A workaround using readline is possible and seems simpler/easier.
– Isaac
14 hours ago
|
show 2 more comments
Caveat - the ERR trap is not inherited to subshells, so(1+2)would need to be'(1+2)'
– Jeff Schaller
yesterday
2
bashandzshhave acommand-not-found-handlerhook (mispelledhandleinbash) that you could also use here.
– Stéphane Chazelas
yesterday
Thanks, Stéphane! I've incorporated the idea.
– Jeff Schaller
yesterday
2
command_not_found_handledoesn't work on something like123/456, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With theERRtrap, I think you could also use$BASH_COMMANDto get the full command line, and not just the last word. That would make expressions with spaces work.
– ilkkachu
yesterday
1
A workaround using readline is possible and seems simpler/easier.
– Isaac
14 hours ago
Caveat - the ERR trap is not inherited to subshells, so
(1+2) would need to be '(1+2)'– Jeff Schaller
yesterday
Caveat - the ERR trap is not inherited to subshells, so
(1+2) would need to be '(1+2)'– Jeff Schaller
yesterday
2
2
bash and zsh have a command-not-found-handler hook (mispelled handle in bash) that you could also use here.– Stéphane Chazelas
yesterday
bash and zsh have a command-not-found-handler hook (mispelled handle in bash) that you could also use here.– Stéphane Chazelas
yesterday
Thanks, Stéphane! I've incorporated the idea.
– Jeff Schaller
yesterday
Thanks, Stéphane! I've incorporated the idea.
– Jeff Schaller
yesterday
2
2
command_not_found_handle doesn't work on something like 123/456, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With the ERR trap, I think you could also use $BASH_COMMAND to get the full command line, and not just the last word. That would make expressions with spaces work.– ilkkachu
yesterday
command_not_found_handle doesn't work on something like 123/456, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With the ERR trap, I think you could also use $BASH_COMMAND to get the full command line, and not just the last word. That would make expressions with spaces work.– ilkkachu
yesterday
1
1
A workaround using readline is possible and seems simpler/easier.
– Isaac
14 hours ago
A workaround using readline is possible and seems simpler/easier.
– Isaac
14 hours ago
|
show 2 more comments
up vote
3
down vote
The following command lines are rather simple to type,
<<< 5+4 bc
<<< 6/3 bc
<<< 7*2 bc
and slightly more complicated with parentheses (must be quoted or escaped),
<<< "(5+4)*2/3" bc
<<< (5+4)*2/3 bc
add a comment |
up vote
3
down vote
The following command lines are rather simple to type,
<<< 5+4 bc
<<< 6/3 bc
<<< 7*2 bc
and slightly more complicated with parentheses (must be quoted or escaped),
<<< "(5+4)*2/3" bc
<<< (5+4)*2/3 bc
add a comment |
up vote
3
down vote
up vote
3
down vote
The following command lines are rather simple to type,
<<< 5+4 bc
<<< 6/3 bc
<<< 7*2 bc
and slightly more complicated with parentheses (must be quoted or escaped),
<<< "(5+4)*2/3" bc
<<< (5+4)*2/3 bc
The following command lines are rather simple to type,
<<< 5+4 bc
<<< 6/3 bc
<<< 7*2 bc
and slightly more complicated with parentheses (must be quoted or escaped),
<<< "(5+4)*2/3" bc
<<< (5+4)*2/3 bc
answered yesterday
sudodus
61116
61116
add a comment |
add a comment |
up vote
1
down vote
Another imperfect way of doing that in Bash would be to use the DEBUG trap, which runs on every command. With extdebug set, the trap handler can prevent the main command from running, so you don't get the "command not found" errors.
$ cat bash_calc.sh
shopt -s extdebug
debug_calc() {
local re='^[ (]*-?[0-9]'
if [[ $BASH_COMMAND =~ $re ]]; then
echo "$BASH_COMMAND" | bc -l
return 1
fi
}
trap debug_calc DEBUG
$ . ./bash_calc.sh
$ 123 * 456
56088
$ 123/456
.26973684210526315789
The trap gets the full command line before expanding variables or filename patterns, so an unquoted * works. (But using shell variables in the calculation doesn't work.)
However, unquoted parenthesis still cause a syntax error, so this is not perfect either.
(I nicked the regex above from Stéphane's answer.)
Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
– Isaac
14 hours ago
@Isaac, hmm, that's not good. I'll admit I'd never tested it for speed, though I wonder if it matters here if this is for interactive use. Anyway, as mentioned, this is just another imperfect idea. I originally wasn't going to post it at all, but there are other somewhat hackish Bash solutions here, too, so I think it can stay.
– ilkkachu
9 hours ago
add a comment |
up vote
1
down vote
Another imperfect way of doing that in Bash would be to use the DEBUG trap, which runs on every command. With extdebug set, the trap handler can prevent the main command from running, so you don't get the "command not found" errors.
$ cat bash_calc.sh
shopt -s extdebug
debug_calc() {
local re='^[ (]*-?[0-9]'
if [[ $BASH_COMMAND =~ $re ]]; then
echo "$BASH_COMMAND" | bc -l
return 1
fi
}
trap debug_calc DEBUG
$ . ./bash_calc.sh
$ 123 * 456
56088
$ 123/456
.26973684210526315789
The trap gets the full command line before expanding variables or filename patterns, so an unquoted * works. (But using shell variables in the calculation doesn't work.)
However, unquoted parenthesis still cause a syntax error, so this is not perfect either.
(I nicked the regex above from Stéphane's answer.)
Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
– Isaac
14 hours ago
@Isaac, hmm, that's not good. I'll admit I'd never tested it for speed, though I wonder if it matters here if this is for interactive use. Anyway, as mentioned, this is just another imperfect idea. I originally wasn't going to post it at all, but there are other somewhat hackish Bash solutions here, too, so I think it can stay.
– ilkkachu
9 hours ago
add a comment |
up vote
1
down vote
up vote
1
down vote
Another imperfect way of doing that in Bash would be to use the DEBUG trap, which runs on every command. With extdebug set, the trap handler can prevent the main command from running, so you don't get the "command not found" errors.
$ cat bash_calc.sh
shopt -s extdebug
debug_calc() {
local re='^[ (]*-?[0-9]'
if [[ $BASH_COMMAND =~ $re ]]; then
echo "$BASH_COMMAND" | bc -l
return 1
fi
}
trap debug_calc DEBUG
$ . ./bash_calc.sh
$ 123 * 456
56088
$ 123/456
.26973684210526315789
The trap gets the full command line before expanding variables or filename patterns, so an unquoted * works. (But using shell variables in the calculation doesn't work.)
However, unquoted parenthesis still cause a syntax error, so this is not perfect either.
(I nicked the regex above from Stéphane's answer.)
Another imperfect way of doing that in Bash would be to use the DEBUG trap, which runs on every command. With extdebug set, the trap handler can prevent the main command from running, so you don't get the "command not found" errors.
$ cat bash_calc.sh
shopt -s extdebug
debug_calc() {
local re='^[ (]*-?[0-9]'
if [[ $BASH_COMMAND =~ $re ]]; then
echo "$BASH_COMMAND" | bc -l
return 1
fi
}
trap debug_calc DEBUG
$ . ./bash_calc.sh
$ 123 * 456
56088
$ 123/456
.26973684210526315789
The trap gets the full command line before expanding variables or filename patterns, so an unquoted * works. (But using shell variables in the calculation doesn't work.)
However, unquoted parenthesis still cause a syntax error, so this is not perfect either.
(I nicked the regex above from Stéphane's answer.)
edited yesterday
answered yesterday
ilkkachu
54.2k782147
54.2k782147
Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
– Isaac
14 hours ago
@Isaac, hmm, that's not good. I'll admit I'd never tested it for speed, though I wonder if it matters here if this is for interactive use. Anyway, as mentioned, this is just another imperfect idea. I originally wasn't going to post it at all, but there are other somewhat hackish Bash solutions here, too, so I think it can stay.
– ilkkachu
9 hours ago
add a comment |
Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
– Isaac
14 hours ago
@Isaac, hmm, that's not good. I'll admit I'd never tested it for speed, though I wonder if it matters here if this is for interactive use. Anyway, as mentioned, this is just another imperfect idea. I originally wasn't going to post it at all, but there are other somewhat hackish Bash solutions here, too, so I think it can stay.
– ilkkachu
9 hours ago
Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
– Isaac
14 hours ago
Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
– Isaac
14 hours ago
@Isaac, hmm, that's not good. I'll admit I'd never tested it for speed, though I wonder if it matters here if this is for interactive use. Anyway, as mentioned, this is just another imperfect idea. I originally wasn't going to post it at all, but there are other somewhat hackish Bash solutions here, too, so I think it can stay.
– ilkkachu
9 hours ago
@Isaac, hmm, that's not good. I'll admit I'd never tested it for speed, though I wonder if it matters here if this is for interactive use. Anyway, as mentioned, this is just another imperfect idea. I originally wasn't going to post it at all, but there are other somewhat hackish Bash solutions here, too, so I think it can stay.
– ilkkachu
9 hours ago
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f486326%2fdo-math-operation-on-the-numbers-typed-into-command-line-without-call-bc%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
4
This reminds me of this question (which isn’t a duplicate).
– Stephen Kitt
yesterday
1
While I've never seen a script called, for example,
2+2, it could conceivably exist, in which case you'd never be able to run it.– Jeff Schaller
yesterday
@JeffSchaller Or a bit more plausibly maybe
2/3. You could still run it by typing something like2+2, if2+2was parsed as an alias forecho $((2+2)).– Gilles
yesterday
1
Wouldn't c 1+2+3 be a good alternative? (Calculate and then you can put anything you want, no spaces needed, same commands as bc if you want)
– Fabby
yesterday
in powershell 6/2 and simply works
– phuclv
yesterday