Do math operation on the numbers typed into command line without call bc












5














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.










share|improve this question




















  • 4




    This reminds me of this question (which isn’t a duplicate).
    – Stephen Kitt
    Dec 6 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 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 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 at 18:56












  • in powershell 6/2 and simply works
    – phuclv
    Dec 7 at 1:22
















5














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.










share|improve this question




















  • 4




    This reminds me of this question (which isn’t a duplicate).
    – Stephen Kitt
    Dec 6 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 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 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 at 18:56












  • in powershell 6/2 and simply works
    – phuclv
    Dec 7 at 1:22














5












5








5


2





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.










share|improve this question















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 7 at 5:32









Isaac

11.1k11648




11.1k11648










asked Dec 6 at 9:48









waldauf

9018




9018








  • 4




    This reminds me of this question (which isn’t a duplicate).
    – Stephen Kitt
    Dec 6 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 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 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 at 18:56












  • in powershell 6/2 and simply works
    – phuclv
    Dec 7 at 1:22














  • 4




    This reminds me of this question (which isn’t a duplicate).
    – Stephen Kitt
    Dec 6 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 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 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 at 18:56












  • in powershell 6/2 and simply works
    – phuclv
    Dec 7 at 1:22








4




4




This reminds me of this question (which isn’t a duplicate).
– Stephen Kitt
Dec 6 at 10:04




This reminds me of this question (which isn’t a duplicate).
– Stephen Kitt
Dec 6 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 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 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 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 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 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 at 18:56














in powershell 6/2 and simply works
– phuclv
Dec 7 at 1:22




in powershell 6/2 and simply works
– phuclv
Dec 7 at 1:22










7 Answers
7






active

oldest

votes


















17














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.






share|improve this answer























  • @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 at 9:40










  • @waldauf The name of the function could perfectly be named _calc or anything else.
    – Isaac
    Dec 7 at 10:28










  • @Isaac Solution with keyboard shortcut looks great! I'll try to do it for bindkey in ZSH. :]
    – waldauf
    Dec 7 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 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 at 11:26



















10














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.






share|improve this answer























  • 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 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 at 0:45








  • 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 at 10:52



















9














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
)





share|improve this answer































    6














    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.






    share|improve this answer























    • Caveat - the ERR trap is not inherited to subshells, so (1+2) would need to be '(1+2)'
      – Jeff Schaller
      Dec 6 at 18:55






    • 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 at 19:27












    • Thanks, Stéphane! I've incorporated the idea.
      – Jeff Schaller
      Dec 6 at 19:49






    • 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 at 20:23






    • 1




      A workaround using readline is possible and seems simpler/easier.
      – Isaac
      Dec 7 at 10:49



















    3














    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





    share|improve this answer





























      1














      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.)






      share|improve this answer























      • Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
        – Isaac
        Dec 7 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 at 16:18





















      0














      What about expr?



      $ expr 6 / 2
      3





      share|improve this answer





















        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
        });


        }
        });














        draft saved

        draft discarded


















        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









        17














        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.






        share|improve this answer























        • @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 at 9:40










        • @waldauf The name of the function could perfectly be named _calc or anything else.
          – Isaac
          Dec 7 at 10:28










        • @Isaac Solution with keyboard shortcut looks great! I'll try to do it for bindkey in ZSH. :]
          – waldauf
          Dec 7 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 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 at 11:26
















        17














        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.






        share|improve this answer























        • @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 at 9:40










        • @waldauf The name of the function could perfectly be named _calc or anything else.
          – Isaac
          Dec 7 at 10:28










        • @Isaac Solution with keyboard shortcut looks great! I'll try to do it for bindkey in ZSH. :]
          – waldauf
          Dec 7 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 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 at 11:26














        17












        17








        17






        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.






        share|improve this answer














        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.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Dec 7 at 11:00

























        answered Dec 6 at 11:07









        Isaac

        11.1k11648




        11.1k11648












        • @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 at 9:40










        • @waldauf The name of the function could perfectly be named _calc or anything else.
          – Isaac
          Dec 7 at 10:28










        • @Isaac Solution with keyboard shortcut looks great! I'll try to do it for bindkey in ZSH. :]
          – waldauf
          Dec 7 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 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 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 at 9:40










        • @waldauf The name of the function could perfectly be named _calc or anything else.
          – Isaac
          Dec 7 at 10:28










        • @Isaac Solution with keyboard shortcut looks great! I'll try to do it for bindkey in ZSH. :]
          – waldauf
          Dec 7 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 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 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 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 at 9:40












        @waldauf The name of the function could perfectly be named _calc or anything else.
        – Isaac
        Dec 7 at 10:28




        @waldauf The name of the function could perfectly be named _calc or anything else.
        – Isaac
        Dec 7 at 10:28












        @Isaac Solution with keyboard shortcut looks great! I'll try to do it for bindkey in ZSH. :]
        – waldauf
        Dec 7 at 11:48




        @Isaac Solution with keyboard shortcut looks great! I'll try to do it for bindkey in ZSH. :]
        – waldauf
        Dec 7 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 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 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 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 at 11:26













        10














        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.






        share|improve this answer























        • 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 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 at 0:45








        • 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 at 10:52
















        10














        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.






        share|improve this answer























        • 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 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 at 0:45








        • 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 at 10:52














        10












        10








        10






        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.






        share|improve this answer














        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.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Dec 6 at 20:57

























        answered Dec 6 at 20:27









        bishop

        2,0262822




        2,0262822












        • 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 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 at 0:45








        • 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 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 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 at 0:45








        • 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 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 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 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 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 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 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 at 10:52











        9














        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
        )





        share|improve this answer




























          9














          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
          )





          share|improve this answer


























            9












            9








            9






            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
            )





            share|improve this answer














            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
            )






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Dec 6 at 21:06

























            answered Dec 6 at 10:32









            Stéphane Chazelas

            299k54563913




            299k54563913























                6














                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.






                share|improve this answer























                • Caveat - the ERR trap is not inherited to subshells, so (1+2) would need to be '(1+2)'
                  – Jeff Schaller
                  Dec 6 at 18:55






                • 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 at 19:27












                • Thanks, Stéphane! I've incorporated the idea.
                  – Jeff Schaller
                  Dec 6 at 19:49






                • 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 at 20:23






                • 1




                  A workaround using readline is possible and seems simpler/easier.
                  – Isaac
                  Dec 7 at 10:49
















                6














                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.






                share|improve this answer























                • Caveat - the ERR trap is not inherited to subshells, so (1+2) would need to be '(1+2)'
                  – Jeff Schaller
                  Dec 6 at 18:55






                • 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 at 19:27












                • Thanks, Stéphane! I've incorporated the idea.
                  – Jeff Schaller
                  Dec 6 at 19:49






                • 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 at 20:23






                • 1




                  A workaround using readline is possible and seems simpler/easier.
                  – Isaac
                  Dec 7 at 10:49














                6












                6








                6






                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.






                share|improve this answer














                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.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Dec 6 at 19:48

























                answered Dec 6 at 18:38









                Jeff Schaller

                38.6k1053125




                38.6k1053125












                • Caveat - the ERR trap is not inherited to subshells, so (1+2) would need to be '(1+2)'
                  – Jeff Schaller
                  Dec 6 at 18:55






                • 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 at 19:27












                • Thanks, Stéphane! I've incorporated the idea.
                  – Jeff Schaller
                  Dec 6 at 19:49






                • 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 at 20:23






                • 1




                  A workaround using readline is possible and seems simpler/easier.
                  – Isaac
                  Dec 7 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 at 18:55






                • 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 at 19:27












                • Thanks, Stéphane! I've incorporated the idea.
                  – Jeff Schaller
                  Dec 6 at 19:49






                • 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 at 20:23






                • 1




                  A workaround using readline is possible and seems simpler/easier.
                  – Isaac
                  Dec 7 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 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 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 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 at 19:27














                Thanks, Stéphane! I've incorporated the idea.
                – Jeff Schaller
                Dec 6 at 19:49




                Thanks, Stéphane! I've incorporated the idea.
                – Jeff Schaller
                Dec 6 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 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 at 20:23




                1




                1




                A workaround using readline is possible and seems simpler/easier.
                – Isaac
                Dec 7 at 10:49




                A workaround using readline is possible and seems simpler/easier.
                – Isaac
                Dec 7 at 10:49











                3














                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





                share|improve this answer


























                  3














                  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





                  share|improve this answer
























                    3












                    3








                    3






                    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





                    share|improve this answer












                    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






                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered Dec 6 at 21:11









                    sudodus

                    1,06116




                    1,06116























                        1














                        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.)






                        share|improve this answer























                        • Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
                          – Isaac
                          Dec 7 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 at 16:18


















                        1














                        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.)






                        share|improve this answer























                        • Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
                          – Isaac
                          Dec 7 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 at 16:18
















                        1












                        1








                        1






                        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.)






                        share|improve this 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.)







                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited Dec 6 at 20:24

























                        answered Dec 6 at 20:18









                        ilkkachu

                        55.5k783151




                        55.5k783151












                        • Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
                          – Isaac
                          Dec 7 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 at 16:18




















                        • Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
                          – Isaac
                          Dec 7 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 at 16:18


















                        Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
                        – Isaac
                        Dec 7 at 10:55




                        Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
                        – Isaac
                        Dec 7 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 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 at 16:18













                        0














                        What about expr?



                        $ expr 6 / 2
                        3





                        share|improve this answer


























                          0














                          What about expr?



                          $ expr 6 / 2
                          3





                          share|improve this answer
























                            0












                            0








                            0






                            What about expr?



                            $ expr 6 / 2
                            3





                            share|improve this answer












                            What about expr?



                            $ expr 6 / 2
                            3






                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered Dec 12 at 4:34









                            Hopping Bunny

                            1341




                            1341






























                                draft saved

                                draft discarded




















































                                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.




                                draft saved


                                draft discarded














                                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





















































                                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







                                Popular posts from this blog

                                Plaza Victoria

                                In PowerPoint, is there a keyboard shortcut for bulleted / numbered list?

                                How to put 3 figures in Latex with 2 figures side by side and 1 below these side by side images but in...