Do math operation on the numbers typed into command line without call bc
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
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
Dec 6 '18 at 10:04
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
Dec 6 '18 at 13:47
@JeffSchaller Or a bit more plausibly maybe2/3
. You could still run it by typing something like2+2
, if2+2
was parsed as an alias forecho $((2+2))
.
– Gilles
Dec 6 '18 at 18:17
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
Dec 6 '18 at 18:56
in powershell 6/2 and simply works
– phuclv
Dec 7 '18 at 1:22
|
show 2 more comments
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 Dec 7 '18 at 5:32
Isaac
11.8k11752
11.8k11752
asked Dec 6 '18 at 9:48
waldaufwaldauf
9018
9018
4
This reminds me of this question (which isn’t a duplicate).
– Stephen Kitt
Dec 6 '18 at 10:04
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
Dec 6 '18 at 13:47
@JeffSchaller Or a bit more plausibly maybe2/3
. You could still run it by typing something like2+2
, if2+2
was parsed as an alias forecho $((2+2))
.
– Gilles
Dec 6 '18 at 18:17
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
Dec 6 '18 at 18:56
in powershell 6/2 and simply works
– phuclv
Dec 7 '18 at 1:22
|
show 2 more comments
4
This reminds me of this question (which isn’t a duplicate).
– Stephen Kitt
Dec 6 '18 at 10:04
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
Dec 6 '18 at 13:47
@JeffSchaller Or a bit more plausibly maybe2/3
. You could still run it by typing something like2+2
, if2+2
was parsed as an alias forecho $((2+2))
.
– Gilles
Dec 6 '18 at 18:17
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
Dec 6 '18 at 18:56
in powershell 6/2 and simply works
– phuclv
Dec 7 '18 at 1:22
4
4
This reminds me of this question (which isn’t a duplicate).
– Stephen Kitt
Dec 6 '18 at 10:04
This reminds me of this question (which isn’t a duplicate).
– Stephen Kitt
Dec 6 '18 at 10:04
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
Dec 6 '18 at 13:47
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
Dec 6 '18 at 13:47
@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
Dec 6 '18 at 18:17
@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
Dec 6 '18 at 18:17
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
Dec 6 '18 at 18:56
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
Dec 6 '18 at 18:56
in powershell 6/2 and simply works
– phuclv
Dec 7 '18 at 1:22
in powershell 6/2 and simply works
– phuclv
Dec 7 '18 at 1:22
|
show 2 more comments
7 Answers
7
active
oldest
votes
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
Dec 7 '18 at 9:40
@waldauf The name of the function could perfectly be named_calc
or anything else.
– Isaac
Dec 7 '18 at 10:28
@Isaac Solution with keyboard shortcut looks great! I'll try to do it forbindkey
in ZSH. :]
– waldauf
Dec 7 '18 at 11:48
@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
Dec 7 '18 at 18:20
@ShreevatsaR - I'm sorry if you understood my question like that. I was just curious if I can type into command line some math example and ZSH/Bash recognizes math and calculate it. That's all. Really. :] And you're right - undrescore is not good name convention. I'll change it. ;]
– waldauf
Dec 9 '18 at 11:26
add a comment |
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
Dec 7 '18 at 0:33
@mosvy Well, can't as written in my answer. But certainlyeval
raises this hack to all-new heights!
– bishop
Dec 7 '18 at 0:45
1
+1 Thanks for your idea, I implemented an alternative in my answer. Note that the value ofHISTTIMEFORMAT
needs to be cleared to avoid the time tag printed byhistory
. Perl seems like too much for this little edition use.
– Isaac
Dec 7 '18 at 10:52
add a comment |
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 |
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
Dec 6 '18 at 18:55
2
bash
andzsh
have acommand-not-found-handler
hook (mispelledhandle
inbash
) that you could also use here.
– Stéphane Chazelas
Dec 6 '18 at 19:27
Thanks, Stéphane! I've incorporated the idea.
– Jeff Schaller
Dec 6 '18 at 19:49
2
command_not_found_handle
doesn't work on something like123/456
, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With theERR
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
Dec 6 '18 at 20:23
1
A workaround using readline is possible and seems simpler/easier.
– Isaac
Dec 7 '18 at 10:49
|
show 2 more comments
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 |
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
Dec 7 '18 at 10:55
@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
Dec 7 '18 at 16:18
add a comment |
What about expr?
$ expr 6 / 2
3
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "106"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
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
7 Answers
7
active
oldest
votes
7 Answers
7
active
oldest
votes
active
oldest
votes
active
oldest
votes
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
Dec 7 '18 at 9:40
@waldauf The name of the function could perfectly be named_calc
or anything else.
– Isaac
Dec 7 '18 at 10:28
@Isaac Solution with keyboard shortcut looks great! I'll try to do it forbindkey
in ZSH. :]
– waldauf
Dec 7 '18 at 11:48
@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
Dec 7 '18 at 18:20
@ShreevatsaR - I'm sorry if you understood my question like that. I was just curious if I can type into command line some math example and ZSH/Bash recognizes math and calculate it. That's all. Really. :] And you're right - undrescore is not good name convention. I'll change it. ;]
– waldauf
Dec 9 '18 at 11:26
add a comment |
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
Dec 7 '18 at 9:40
@waldauf The name of the function could perfectly be named_calc
or anything else.
– Isaac
Dec 7 '18 at 10:28
@Isaac Solution with keyboard shortcut looks great! I'll try to do it forbindkey
in ZSH. :]
– waldauf
Dec 7 '18 at 11:48
@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
Dec 7 '18 at 18:20
@ShreevatsaR - I'm sorry if you understood my question like that. I was just curious if I can type into command line some math example and ZSH/Bash recognizes math and calculate it. That's all. Really. :] And you're right - undrescore is not good name convention. I'll change it. ;]
– waldauf
Dec 9 '18 at 11:26
add a comment |
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 Dec 7 '18 at 11:00
answered Dec 6 '18 at 11:07
IsaacIsaac
11.8k11752
11.8k11752
@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
Dec 7 '18 at 9:40
@waldauf The name of the function could perfectly be named_calc
or anything else.
– Isaac
Dec 7 '18 at 10:28
@Isaac Solution with keyboard shortcut looks great! I'll try to do it forbindkey
in ZSH. :]
– waldauf
Dec 7 '18 at 11:48
@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
Dec 7 '18 at 18:20
@ShreevatsaR - I'm sorry if you understood my question like that. I was just curious if I can type into command line some math example and ZSH/Bash recognizes math and calculate it. That's all. Really. :] And you're right - undrescore is not good name convention. I'll change it. ;]
– waldauf
Dec 9 '18 at 11:26
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
Dec 7 '18 at 9:40
@waldauf The name of the function could perfectly be named_calc
or anything else.
– Isaac
Dec 7 '18 at 10:28
@Isaac Solution with keyboard shortcut looks great! I'll try to do it forbindkey
in ZSH. :]
– waldauf
Dec 7 '18 at 11:48
@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
Dec 7 '18 at 18:20
@ShreevatsaR - I'm sorry if you understood my question like that. I was just curious if I can type into command line some math example and ZSH/Bash recognizes math and calculate it. That's all. Really. :] And you're right - undrescore is not good name convention. I'll change it. ;]
– waldauf
Dec 9 '18 at 11:26
@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
Dec 7 '18 at 9:40
@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
Dec 7 '18 at 9:40
@waldauf The name of the function could perfectly be named
_calc
or anything else.– Isaac
Dec 7 '18 at 10:28
@waldauf The name of the function could perfectly be named
_calc
or anything else.– Isaac
Dec 7 '18 at 10:28
@Isaac Solution with keyboard shortcut looks great! I'll try to do it for
bindkey
in ZSH. :]– waldauf
Dec 7 '18 at 11:48
@Isaac Solution with keyboard shortcut looks great! I'll try to do it for
bindkey
in ZSH. :]– waldauf
Dec 7 '18 at 11:48
@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
Dec 7 '18 at 18:20
@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
Dec 7 '18 at 18:20
@ShreevatsaR - I'm sorry if you understood my question like that. I was just curious if I can type into command line some math example and ZSH/Bash recognizes math and calculate it. That's all. Really. :] And you're right - undrescore is not good name convention. I'll change it. ;]
– waldauf
Dec 9 '18 at 11:26
@ShreevatsaR - I'm sorry if you understood my question like that. I was just curious if I can type into command line some math example and ZSH/Bash recognizes math and calculate it. That's all. Really. :] And you're right - undrescore is not good name convention. I'll change it. ;]
– waldauf
Dec 9 '18 at 11:26
add a comment |
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
Dec 7 '18 at 0:33
@mosvy Well, can't as written in my answer. But certainlyeval
raises this hack to all-new heights!
– bishop
Dec 7 '18 at 0:45
1
+1 Thanks for your idea, I implemented an alternative in my answer. Note that the value ofHISTTIMEFORMAT
needs to be cleared to avoid the time tag printed byhistory
. Perl seems like too much for this little edition use.
– Isaac
Dec 7 '18 at 10:52
add a comment |
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
Dec 7 '18 at 0:33
@mosvy Well, can't as written in my answer. But certainlyeval
raises this hack to all-new heights!
– bishop
Dec 7 '18 at 0:45
1
+1 Thanks for your idea, I implemented an alternative in my answer. Note that the value ofHISTTIMEFORMAT
needs to be cleared to avoid the time tag printed byhistory
. Perl seems like too much for this little edition use.
– Isaac
Dec 7 '18 at 10:52
add a comment |
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 Dec 6 '18 at 20:57
answered Dec 6 '18 at 20:27
bishopbishop
2,1062822
2,1062822
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
Dec 7 '18 at 0:33
@mosvy Well, can't as written in my answer. But certainlyeval
raises this hack to all-new heights!
– bishop
Dec 7 '18 at 0:45
1
+1 Thanks for your idea, I implemented an alternative in my answer. Note that the value ofHISTTIMEFORMAT
needs to be cleared to avoid the time tag printed byhistory
. Perl seems like too much for this little edition use.
– Isaac
Dec 7 '18 at 10:52
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
Dec 7 '18 at 0:33
@mosvy Well, can't as written in my answer. But certainlyeval
raises this hack to all-new heights!
– bishop
Dec 7 '18 at 0:45
1
+1 Thanks for your idea, I implemented an alternative in my answer. Note that the value ofHISTTIMEFORMAT
needs to be cleared to avoid the time tag printed byhistory
. Perl seems like too much for this little edition use.
– Isaac
Dec 7 '18 at 10:52
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
Dec 7 '18 at 0:33
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
Dec 7 '18 at 0:33
@mosvy Well, can't as written in my answer. But certainly
eval
raises this hack to all-new heights!– bishop
Dec 7 '18 at 0:45
@mosvy Well, can't as written in my answer. But certainly
eval
raises this hack to all-new heights!– bishop
Dec 7 '18 at 0:45
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
Dec 7 '18 at 10:52
+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
Dec 7 '18 at 10:52
add a comment |
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 |
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 |
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 Dec 6 '18 at 21:06
answered Dec 6 '18 at 10:32
Stéphane ChazelasStéphane Chazelas
304k57570927
304k57570927
add a comment |
add a comment |
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
Dec 6 '18 at 18:55
2
bash
andzsh
have acommand-not-found-handler
hook (mispelledhandle
inbash
) that you could also use here.
– Stéphane Chazelas
Dec 6 '18 at 19:27
Thanks, Stéphane! I've incorporated the idea.
– Jeff Schaller
Dec 6 '18 at 19:49
2
command_not_found_handle
doesn't work on something like123/456
, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With theERR
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
Dec 6 '18 at 20:23
1
A workaround using readline is possible and seems simpler/easier.
– Isaac
Dec 7 '18 at 10:49
|
show 2 more comments
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
Dec 6 '18 at 18:55
2
bash
andzsh
have acommand-not-found-handler
hook (mispelledhandle
inbash
) that you could also use here.
– Stéphane Chazelas
Dec 6 '18 at 19:27
Thanks, Stéphane! I've incorporated the idea.
– Jeff Schaller
Dec 6 '18 at 19:49
2
command_not_found_handle
doesn't work on something like123/456
, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With theERR
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
Dec 6 '18 at 20:23
1
A workaround using readline is possible and seems simpler/easier.
– Isaac
Dec 7 '18 at 10:49
|
show 2 more comments
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 Dec 6 '18 at 19:48
answered Dec 6 '18 at 18:38
Jeff SchallerJeff Schaller
40.8k1056129
40.8k1056129
Caveat - the ERR trap is not inherited to subshells, so(1+2)
would need to be'(1+2)'
– Jeff Schaller
Dec 6 '18 at 18:55
2
bash
andzsh
have acommand-not-found-handler
hook (mispelledhandle
inbash
) that you could also use here.
– Stéphane Chazelas
Dec 6 '18 at 19:27
Thanks, Stéphane! I've incorporated the idea.
– Jeff Schaller
Dec 6 '18 at 19:49
2
command_not_found_handle
doesn't work on something like123/456
, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With theERR
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
Dec 6 '18 at 20:23
1
A workaround using readline is possible and seems simpler/easier.
– Isaac
Dec 7 '18 at 10:49
|
show 2 more comments
Caveat - the ERR trap is not inherited to subshells, so(1+2)
would need to be'(1+2)'
– Jeff Schaller
Dec 6 '18 at 18:55
2
bash
andzsh
have acommand-not-found-handler
hook (mispelledhandle
inbash
) that you could also use here.
– Stéphane Chazelas
Dec 6 '18 at 19:27
Thanks, Stéphane! I've incorporated the idea.
– Jeff Schaller
Dec 6 '18 at 19:49
2
command_not_found_handle
doesn't work on something like123/456
, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With theERR
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
Dec 6 '18 at 20:23
1
A workaround using readline is possible and seems simpler/easier.
– Isaac
Dec 7 '18 at 10:49
Caveat - the ERR trap is not inherited to subshells, so
(1+2)
would need to be '(1+2)'
– Jeff Schaller
Dec 6 '18 at 18:55
Caveat - the ERR trap is not inherited to subshells, so
(1+2)
would need to be '(1+2)'
– Jeff Schaller
Dec 6 '18 at 18:55
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
Dec 6 '18 at 19:27
bash
and zsh
have a command-not-found-handler
hook (mispelled handle
in bash
) that you could also use here.– Stéphane Chazelas
Dec 6 '18 at 19:27
Thanks, Stéphane! I've incorporated the idea.
– Jeff Schaller
Dec 6 '18 at 19:49
Thanks, Stéphane! I've incorporated the idea.
– Jeff Schaller
Dec 6 '18 at 19:49
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
Dec 6 '18 at 20:23
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
Dec 6 '18 at 20:23
1
1
A workaround using readline is possible and seems simpler/easier.
– Isaac
Dec 7 '18 at 10:49
A workaround using readline is possible and seems simpler/easier.
– Isaac
Dec 7 '18 at 10:49
|
show 2 more comments
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 |
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 |
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 Dec 6 '18 at 21:11
sudodussudodus
1,49337
1,49337
add a comment |
add a comment |
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
Dec 7 '18 at 10:55
@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
Dec 7 '18 at 16:18
add a comment |
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
Dec 7 '18 at 10:55
@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
Dec 7 '18 at 16:18
add a comment |
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 Dec 6 '18 at 20:24
answered Dec 6 '18 at 20:18
ilkkachuilkkachu
57.8k888163
57.8k888163
Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
– Isaac
Dec 7 '18 at 10:55
@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
Dec 7 '18 at 16:18
add a comment |
Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
– Isaac
Dec 7 '18 at 10:55
@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
Dec 7 '18 at 16:18
Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
– Isaac
Dec 7 '18 at 10:55
Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
– Isaac
Dec 7 '18 at 10:55
@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
Dec 7 '18 at 16:18
@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
Dec 7 '18 at 16:18
add a comment |
What about expr?
$ expr 6 / 2
3
add a comment |
What about expr?
$ expr 6 / 2
3
add a comment |
What about expr?
$ expr 6 / 2
3
What about expr?
$ expr 6 / 2
3
answered Dec 12 '18 at 4:34
Hopping BunnyHopping Bunny
1441
1441
add a comment |
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.
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
Dec 6 '18 at 10:04
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
Dec 6 '18 at 13:47
@JeffSchaller Or a bit more plausibly maybe
2/3
. You could still run it by typing something like2+2
, if2+2
was parsed as an alias forecho $((2+2))
.– Gilles
Dec 6 '18 at 18:17
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
Dec 6 '18 at 18:56
in powershell 6/2 and simply works
– phuclv
Dec 7 '18 at 1:22