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

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
6
down vote

favorite
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



















  • 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














up vote
6
down vote

favorite
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



















  • 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












up vote
6
down vote

favorite
2









up vote
6
down vote

favorite
2






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

11k11648




11k11648










asked Dec 6 at 9:48









waldauf

9518




9518







  • 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

















up vote
16
down vote



accepted










Shortcut Alt-c (bash)



With bash, using the readline utility, we can define a key sequence to place the word calc at the start and enclose the text written so far into double quotes:



 bind '"ec": "C-acalc "e[F""'


Having executed that, you type 23 + 46 * 89 for example, then Alt-c to get:



 calc "23 + 46 * 89"


Just press enter and the math will be executed by the function defined as calc, which could be as simple as, or a lot more complex:



 calc () <<<"$*" bc -l; 


a (+) Alias



We can define an alias:



alias +='calc #'


Which will comment the whole command line typed so far. You type:



 + (56 * 23 + 26) / 17


When you press enter, the line will be converted to calc #(56 * 23 + 26) / 17 and the command calc will be called. If calc is this function:



bash



 calc() s=$(HISTTIMEFORMAT='' history 1); # recover last command line.
s=$s#*[ ]; # remove initial spaces.
s=$s#*[0-9]; # remove history line number.
s=$s#*[ ]+; # remove more spaces.
eval 'bc -l <<<"'"$s"'"'; # calculate the line.



ksh



 calc() s=$(history -1 


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 Added a readline solution (no + needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
    – Isaac
    Dec 7 at 10:24










  • @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

















up vote
10
down vote













I use a variant of bash's magic alias hack:



asis() 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() 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

















up vote
9
down vote













In zsh, you could do something like:



autoload zcalc
accept-line()
if [[ $BUFFER =~ '^[ (]*[+-]? *(0[xX]
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





























    up vote
    6
    down vote













    Inspired by Stéphane's zsh answer, and longer than Isaac's bash answer in total but shorter in operation:



    trap '[[ $_ =~ [[:digit:]] ]] && bc -l <<< "$_"' ERR


    This also has the side effect of showing a "No such file or directory" error each time:



    $ foozle
    -bash: foozle: command not found
    $ 1+2+3
    -bash: 1+2+3: command not found
    6
    $ 6/3
    -bash: 6/3: No such file or directory
    2.00000000000000000000


    The regex could be tightened, depending on the operations you expect to perform.



    This (ab)uses the bash behavior of calling the ERR trap when a given command does not exist. If the last command (in $_) contains a digit, then it executes bc on that "command".




    Thanks to a hint from Stéphane, here's a slightly cleaner way to achieve the result (requires bash 4.0 or later, which introduced the functionality):



    if ! declare -F command_not_found_handle > /dev/null
    then
    command_not_found_handle()
    if [[ "$@" =~ [[:digit:]] ]]; then
    bc <<< "$@";
    else
    printf 'bash: %s: command not foundn' "$1" >&2
    return 127
    fi

    else
    echo Unable to set up the handler function, sorry
    fi


    The function is called any time a command isn't found. If that command contains a digit, we throw it through bc; otherwise, we emit a message similar to bash's stock message and return a 127 exit code.






    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

















    up vote
    3
    down vote













    The following command lines are rather simple to type,



    <<< 5+4 bc
    <<< 6/3 bc
    <<< 7*2 bc


    and slightly more complicated with parentheses (must be quoted or escaped),



    <<< "(5+4)*2/3" bc
    <<< (5+4)*2/3 bc





    share|improve this answer



























      up vote
      1
      down vote













      Another imperfect way of doing that in Bash would be to use the DEBUG trap, which runs on every command. With extdebug set, the trap handler can prevent the main command from running, so you don't get the "command not found" errors.



      $ cat bash_calc.sh
      shopt -s extdebug
      debug_calc() 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


















      up vote
      0
      down vote













      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',
        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








        up vote
        16
        down vote



        accepted










        Shortcut Alt-c (bash)



        With bash, using the readline utility, we can define a key sequence to place the word calc at the start and enclose the text written so far into double quotes:



         bind '"ec": "C-acalc "e[F""'


        Having executed that, you type 23 + 46 * 89 for example, then Alt-c to get:



         calc "23 + 46 * 89"


        Just press enter and the math will be executed by the function defined as calc, which could be as simple as, or a lot more complex:



         calc () <<<"$*" bc -l; 


        a (+) Alias



        We can define an alias:



        alias +='calc #'


        Which will comment the whole command line typed so far. You type:



         + (56 * 23 + 26) / 17


        When you press enter, the line will be converted to calc #(56 * 23 + 26) / 17 and the command calc will be called. If calc is this function:



        bash



         calc() s=$(HISTTIMEFORMAT='' history 1); # recover last command line.
        s=$s#*[ ]; # remove initial spaces.
        s=$s#*[0-9]; # remove history line number.
        s=$s#*[ ]+; # remove more spaces.
        eval 'bc -l <<<"'"$s"'"'; # calculate the line.



        ksh



         calc() s=$(history -1 


        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 Added a readline solution (no + needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
          – Isaac
          Dec 7 at 10:24










        • @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














        up vote
        16
        down vote



        accepted










        Shortcut Alt-c (bash)



        With bash, using the readline utility, we can define a key sequence to place the word calc at the start and enclose the text written so far into double quotes:



         bind '"ec": "C-acalc "e[F""'


        Having executed that, you type 23 + 46 * 89 for example, then Alt-c to get:



         calc "23 + 46 * 89"


        Just press enter and the math will be executed by the function defined as calc, which could be as simple as, or a lot more complex:



         calc () <<<"$*" bc -l; 


        a (+) Alias



        We can define an alias:



        alias +='calc #'


        Which will comment the whole command line typed so far. You type:



         + (56 * 23 + 26) / 17


        When you press enter, the line will be converted to calc #(56 * 23 + 26) / 17 and the command calc will be called. If calc is this function:



        bash



         calc() s=$(HISTTIMEFORMAT='' history 1); # recover last command line.
        s=$s#*[ ]; # remove initial spaces.
        s=$s#*[0-9]; # remove history line number.
        s=$s#*[ ]+; # remove more spaces.
        eval 'bc -l <<<"'"$s"'"'; # calculate the line.



        ksh



         calc() s=$(history -1 


        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 Added a readline solution (no + needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
          – Isaac
          Dec 7 at 10:24










        • @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












        up vote
        16
        down vote



        accepted







        up vote
        16
        down vote



        accepted






        Shortcut Alt-c (bash)



        With bash, using the readline utility, we can define a key sequence to place the word calc at the start and enclose the text written so far into double quotes:



         bind '"ec": "C-acalc "e[F""'


        Having executed that, you type 23 + 46 * 89 for example, then Alt-c to get:



         calc "23 + 46 * 89"


        Just press enter and the math will be executed by the function defined as calc, which could be as simple as, or a lot more complex:



         calc () <<<"$*" bc -l; 


        a (+) Alias



        We can define an alias:



        alias +='calc #'


        Which will comment the whole command line typed so far. You type:



         + (56 * 23 + 26) / 17


        When you press enter, the line will be converted to calc #(56 * 23 + 26) / 17 and the command calc will be called. If calc is this function:



        bash



         calc() s=$(HISTTIMEFORMAT='' history 1); # recover last command line.
        s=$s#*[ ]; # remove initial spaces.
        s=$s#*[0-9]; # remove history line number.
        s=$s#*[ ]+; # remove more spaces.
        eval 'bc -l <<<"'"$s"'"'; # calculate the line.



        ksh



         calc() s=$(history -1 


        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 


        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

        11k11648




        11k11648











        • @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 Added a readline solution (no + needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
          – Isaac
          Dec 7 at 10:24










        • @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
















        • @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 Added a readline solution (no + needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
          – Isaac
          Dec 7 at 10:24










        • @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















        @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 Added a readline solution (no + needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
        – Isaac
        Dec 7 at 10:24




        @waldauf Added a readline solution (no + needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
        – Isaac
        Dec 7 at 10:24












        @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












        up vote
        10
        down vote













        I use a variant of bash's magic alias hack:



        asis() 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() 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














        up vote
        10
        down vote













        I use a variant of bash's magic alias hack:



        asis() 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() 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












        up vote
        10
        down vote










        up vote
        10
        down vote









        I use a variant of bash's magic alias hack:



        asis() 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() 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() 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() 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() 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() 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










        up vote
        9
        down vote













        In zsh, you could do something like:



        autoload zcalc
        accept-line()
        if [[ $BUFFER =~ '^[ (]*[+-]? *(0[xX]
        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


























          up vote
          9
          down vote













          In zsh, you could do something like:



          autoload zcalc
          accept-line()
          if [[ $BUFFER =~ '^[ (]*[+-]? *(0[xX]
          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
























            up vote
            9
            down vote










            up vote
            9
            down vote









            In zsh, you could do something like:



            autoload zcalc
            accept-line()
            if [[ $BUFFER =~ '^[ (]*[+-]? *(0[xX]
            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]
            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

            297k54562908




            297k54562908




















                up vote
                6
                down vote













                Inspired by Stéphane's zsh answer, and longer than Isaac's bash answer in total but shorter in operation:



                trap '[[ $_ =~ [[:digit:]] ]] && bc -l <<< "$_"' ERR


                This also has the side effect of showing a "No such file or directory" error each time:



                $ foozle
                -bash: foozle: command not found
                $ 1+2+3
                -bash: 1+2+3: command not found
                6
                $ 6/3
                -bash: 6/3: No such file or directory
                2.00000000000000000000


                The regex could be tightened, depending on the operations you expect to perform.



                This (ab)uses the bash behavior of calling the ERR trap when a given command does not exist. If the last command (in $_) contains a digit, then it executes bc on that "command".




                Thanks to a hint from Stéphane, here's a slightly cleaner way to achieve the result (requires bash 4.0 or later, which introduced the functionality):



                if ! declare -F command_not_found_handle > /dev/null
                then
                command_not_found_handle()
                if [[ "$@" =~ [[:digit:]] ]]; then
                bc <<< "$@";
                else
                printf 'bash: %s: command not foundn' "$1" >&2
                return 127
                fi

                else
                echo Unable to set up the handler function, sorry
                fi


                The function is called any time a command isn't found. If that command contains a digit, we throw it through bc; otherwise, we emit a message similar to bash's stock message and return a 127 exit code.






                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














                up vote
                6
                down vote













                Inspired by Stéphane's zsh answer, and longer than Isaac's bash answer in total but shorter in operation:



                trap '[[ $_ =~ [[:digit:]] ]] && bc -l <<< "$_"' ERR


                This also has the side effect of showing a "No such file or directory" error each time:



                $ foozle
                -bash: foozle: command not found
                $ 1+2+3
                -bash: 1+2+3: command not found
                6
                $ 6/3
                -bash: 6/3: No such file or directory
                2.00000000000000000000


                The regex could be tightened, depending on the operations you expect to perform.



                This (ab)uses the bash behavior of calling the ERR trap when a given command does not exist. If the last command (in $_) contains a digit, then it executes bc on that "command".




                Thanks to a hint from Stéphane, here's a slightly cleaner way to achieve the result (requires bash 4.0 or later, which introduced the functionality):



                if ! declare -F command_not_found_handle > /dev/null
                then
                command_not_found_handle()
                if [[ "$@" =~ [[:digit:]] ]]; then
                bc <<< "$@";
                else
                printf 'bash: %s: command not foundn' "$1" >&2
                return 127
                fi

                else
                echo Unable to set up the handler function, sorry
                fi


                The function is called any time a command isn't found. If that command contains a digit, we throw it through bc; otherwise, we emit a message similar to bash's stock message and return a 127 exit code.






                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












                up vote
                6
                down vote










                up vote
                6
                down vote









                Inspired by Stéphane's zsh answer, and longer than Isaac's bash answer in total but shorter in operation:



                trap '[[ $_ =~ [[:digit:]] ]] && bc -l <<< "$_"' ERR


                This also has the side effect of showing a "No such file or directory" error each time:



                $ foozle
                -bash: foozle: command not found
                $ 1+2+3
                -bash: 1+2+3: command not found
                6
                $ 6/3
                -bash: 6/3: No such file or directory
                2.00000000000000000000


                The regex could be tightened, depending on the operations you expect to perform.



                This (ab)uses the bash behavior of calling the ERR trap when a given command does not exist. If the last command (in $_) contains a digit, then it executes bc on that "command".




                Thanks to a hint from Stéphane, here's a slightly cleaner way to achieve the result (requires bash 4.0 or later, which introduced the functionality):



                if ! declare -F command_not_found_handle > /dev/null
                then
                command_not_found_handle()
                if [[ "$@" =~ [[:digit:]] ]]; then
                bc <<< "$@";
                else
                printf 'bash: %s: command not foundn' "$1" >&2
                return 127
                fi

                else
                echo Unable to set up the handler function, sorry
                fi


                The function is called any time a command isn't found. If that command contains a digit, we throw it through bc; otherwise, we emit a message similar to bash's stock message and return a 127 exit code.






                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

                37.9k1053123




                37.9k1053123











                • 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










                up vote
                3
                down vote













                The following command lines are rather simple to type,



                <<< 5+4 bc
                <<< 6/3 bc
                <<< 7*2 bc


                and slightly more complicated with parentheses (must be quoted or escaped),



                <<< "(5+4)*2/3" bc
                <<< (5+4)*2/3 bc





                share|improve this answer
























                  up vote
                  3
                  down vote













                  The following command lines are rather simple to type,



                  <<< 5+4 bc
                  <<< 6/3 bc
                  <<< 7*2 bc


                  and slightly more complicated with parentheses (must be quoted or escaped),



                  <<< "(5+4)*2/3" bc
                  <<< (5+4)*2/3 bc





                  share|improve this answer






















                    up vote
                    3
                    down vote










                    up vote
                    3
                    down vote









                    The following command lines are rather simple to type,



                    <<< 5+4 bc
                    <<< 6/3 bc
                    <<< 7*2 bc


                    and slightly more complicated with parentheses (must be quoted or escaped),



                    <<< "(5+4)*2/3" bc
                    <<< (5+4)*2/3 bc





                    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

                    74616




                    74616




















                        up vote
                        1
                        down vote













                        Another imperfect way of doing that in Bash would be to use the DEBUG trap, which runs on every command. With extdebug set, the trap handler can prevent the main command from running, so you don't get the "command not found" errors.



                        $ cat bash_calc.sh
                        shopt -s extdebug
                        debug_calc() 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















                        up vote
                        1
                        down vote













                        Another imperfect way of doing that in Bash would be to use the DEBUG trap, which runs on every command. With extdebug set, the trap handler can prevent the main command from running, so you don't get the "command not found" errors.



                        $ cat bash_calc.sh
                        shopt -s extdebug
                        debug_calc() 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













                        up vote
                        1
                        down vote










                        up vote
                        1
                        down vote









                        Another imperfect way of doing that in Bash would be to use the DEBUG trap, which runs on every command. With extdebug set, the trap handler can prevent the main command from running, so you don't get the "command not found" errors.



                        $ cat bash_calc.sh
                        shopt -s extdebug
                        debug_calc() 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() 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

                        54.9k782149




                        54.9k782149











                        • 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











                        up vote
                        0
                        down vote













                        What about expr?



                        $ expr 6 / 2
                        3





                        share|improve this answer
























                          up vote
                          0
                          down vote













                          What about expr?



                          $ expr 6 / 2
                          3





                          share|improve this answer






















                            up vote
                            0
                            down vote










                            up vote
                            0
                            down vote









                            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

                                How to check contact read email or not when send email to Individual?

                                Bahrain

                                Postfix configuration issue with fips on centos 7; mailgun relay