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











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
    yesterday






  • 1




    While I've never seen a script called, for example, 2+2, it could conceivably exist, in which case you'd never be able to run it.
    – Jeff Schaller
    yesterday










  • @JeffSchaller Or a bit more plausibly maybe 2/3. You could still run it by typing something like 2+2, if 2+2 was parsed as an alias for echo $((2+2)).
    – Gilles
    yesterday






  • 1




    Wouldn't c 1+2+3 be a good alternative? (Calculate and then you can put anything you want, no spaces needed, same commands as bc if you want)
    – Fabby
    yesterday












  • in powershell 6/2 and simply works
    – phuclv
    yesterday















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
    yesterday






  • 1




    While I've never seen a script called, for example, 2+2, it could conceivably exist, in which case you'd never be able to run it.
    – Jeff Schaller
    yesterday










  • @JeffSchaller Or a bit more plausibly maybe 2/3. You could still run it by typing something like 2+2, if 2+2 was parsed as an alias for echo $((2+2)).
    – Gilles
    yesterday






  • 1




    Wouldn't c 1+2+3 be a good alternative? (Calculate and then you can put anything you want, no spaces needed, same commands as bc if you want)
    – Fabby
    yesterday












  • in powershell 6/2 and simply works
    – phuclv
    yesterday













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 20 hours ago









Isaac

10.6k11447




10.6k11447










asked yesterday









waldauf

9517




9517








  • 4




    This reminds me of this question (which isn’t a duplicate).
    – Stephen Kitt
    yesterday






  • 1




    While I've never seen a script called, for example, 2+2, it could conceivably exist, in which case you'd never be able to run it.
    – Jeff Schaller
    yesterday










  • @JeffSchaller Or a bit more plausibly maybe 2/3. You could still run it by typing something like 2+2, if 2+2 was parsed as an alias for echo $((2+2)).
    – Gilles
    yesterday






  • 1




    Wouldn't c 1+2+3 be a good alternative? (Calculate and then you can put anything you want, no spaces needed, same commands as bc if you want)
    – Fabby
    yesterday












  • in powershell 6/2 and simply works
    – phuclv
    yesterday














  • 4




    This reminds me of this question (which isn’t a duplicate).
    – Stephen Kitt
    yesterday






  • 1




    While I've never seen a script called, for example, 2+2, it could conceivably exist, in which case you'd never be able to run it.
    – Jeff Schaller
    yesterday










  • @JeffSchaller Or a bit more plausibly maybe 2/3. You could still run it by typing something like 2+2, if 2+2 was parsed as an alias for echo $((2+2)).
    – Gilles
    yesterday






  • 1




    Wouldn't c 1+2+3 be a good alternative? (Calculate and then you can put anything you want, no spaces needed, same commands as bc if you want)
    – Fabby
    yesterday












  • in powershell 6/2 and simply works
    – phuclv
    yesterday








4




4




This reminds me of this question (which isn’t a duplicate).
– Stephen Kitt
yesterday




This reminds me of this question (which isn’t a duplicate).
– Stephen Kitt
yesterday




1




1




While I've never seen a script called, for example, 2+2, it could conceivably exist, in which case you'd never be able to run it.
– Jeff Schaller
yesterday




While I've never seen a script called, for example, 2+2, it could conceivably exist, in which case you'd never be able to run it.
– Jeff Schaller
yesterday












@JeffSchaller Or a bit more plausibly maybe 2/3. You could still run it by typing something like 2+2, if 2+2 was parsed as an alias for echo $((2+2)).
– Gilles
yesterday




@JeffSchaller Or a bit more plausibly maybe 2/3. You could still run it by typing something like 2+2, if 2+2 was parsed as an alias for echo $((2+2)).
– Gilles
yesterday




1




1




Wouldn't c 1+2+3 be a good alternative? (Calculate and then you can put anything you want, no spaces needed, same commands as bc if you want)
– Fabby
yesterday






Wouldn't c 1+2+3 be a good alternative? (Calculate and then you can put anything you want, no spaces needed, same commands as bc if you want)
– Fabby
yesterday














in powershell 6/2 and simply works
– phuclv
yesterday




in powershell 6/2 and simply works
– phuclv
yesterday










6 Answers
6






active

oldest

votes

















up vote
15
down vote



accepted










Shortcut Alt-c (bash)



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



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


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



 calc "23 + 46 * 89"


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



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


a (+) Alias



We can define an alias:



alias +='calc #'


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



 + (56 * 23 + 26) / 17


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



bash



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


ksh



 calc(){ s=$(history -1 |                          # last command(s)
sed '$!d;s/^[ t]*[0-9]*[ t]*+ //'); # clean it up
# (assume one line commads)
eval 'bc -l <<<"'"$s"'"'; # Do the math.
}


zsh zsh doesn't allow neither a + alias nor a # character.



The value will be printed as:



 $ + (56 * 23 + 26) / 17
77.29411764705882352941


Only a + is required, String is quoted (no globs), shell variables accepted:



 $ a=23
$ + (56 * 23 + $a) / 17
77.11764705882352941176


a (+) Function



With some limitations, this is the closest I got to your request with a function (in bash):



+() { bc -l <<< "$*"; }


Which will work like this:



$ + 25+68+8/24
93.33333333333333333333


The problem is that the shell parsing isn't avoided and a * (for example) could get expanded to the list of files in the pwd.



If you write the command line without (white) spaces you will probably be ok.



Beware of writing things like $(...) because they will get expanded.



The safe solution is to quote the string to be evaluated:



$ + '45 + (58+3 * l(23))/7'
54.62949752111249272462

$ + '4 * a(1) * 2'
6.28318530717958647688


Which is only two characters shorter that your _bc "6/2", but a + seems more intuitive to me.






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
    15 hours ago










  • @waldauf Added a readline solution (no + needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
    – Isaac
    15 hours ago










  • @waldauf The name of the function could perfectly be named _calc or anything else.
    – Isaac
    15 hours ago










  • @Isaac Solution with keyboard shortcut looks great! I'll try to do it for bindkey in ZSH. :]
    – waldauf
    13 hours ago










  • @waldauf It seems a bit strange to both pick a convention that has an underscore at the beginning of all names (may require the shift key!) and complain about _bc taking a long time to type :-)
    – ShreevatsaR
    7 hours ago


















up vote
10
down vote













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



asis() { bc <<< "$(history 1 | perl -pe 's/^ *[0-9]+ +[^ ]+ //')"; }
alias c='asis #'


Then:



$ c 1+1
2
$ c -10 + 20 / 5
-6
$ c (-10 + 20) / 5
2
$ c 2^8 / 13
19
$ c scale=5; 2^8 / 13
19.69230


The magic is the fact that alias expansion happens before the usual command line processing, which allows us to create a command whose remaining arguments follow a comment character, that the implementing function finds with the history command.



This magic allows me to type *, (, and other characters literally. But that also means I can't use shell variables because $ is also literal:



$ x=5.0
$ y=-1.2
$ z=4.7
$ c ($x + $y) > $z
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $


I get around this by a bit of bootstrapping:



$ echo "x=$x; y=$y; z=$z"
x=5.0; y=-1.2; z=4.7
$ c x=5.0; y=-1.2; z=4.7; (x + y) > z
0


You might just be better off typing: bc Enter 1 + 1 Enter Control+D





As a side note, I have my default bc settings (like scale) in $HOME/.bc and I use bc -l in the alias. Your use may not require these modifications.






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
    yesterday












  • @mosvy Well, can't as written in my answer. But certainly eval raises this hack to all-new heights!
    – bishop
    yesterday








  • 1




    +1 Thanks for your idea, I implemented an alternative in my answer. Note that the value of HISTTIMEFORMAT needs to be cleared to avoid the time tag printed by history. Perl seems like too much for this little edition use.
    – Isaac
    14 hours ago


















up vote
8
down vote













In zsh, you could do something like:



autoload zcalc
accept-line() {
if [[ $BUFFER =~ '^[ (]*[+-]? *(0[xX]|.)?[[:digit:]]+[^[:alnum:]]' ]]; then
echo
zcalc -e $BUFFER
print -rs -- $BUFFER
BUFFER=
fi
zle .$WIDGET
}
zle -N accept-line


It redefines the accept-line widget (mapped on Enter) to a user-defined widget that checks if the current line starts with a number (decimal or hexadecimal) optionally prefixed with any number of (s, looking for a non-alnum character after that to avoid false positives for commands like 7zip or 411toppm.



If that matches then we pass it to zcalc (more useful than bc in that it can use shell variables and all of zsh math functions and number styles, but does not support arbitrary precision), add the line to history and accept an empty command.



Note that it can cause confusion if you enter a line with digits in things like:



cat << EOF
213 whatever
EOF


Or:



var=(
123 456
)





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
      yesterday






    • 2




      bash and zsh have a command-not-found-handler hook (mispelled handle in bash) that you could also use here.
      – Stéphane Chazelas
      yesterday












    • Thanks, Stéphane! I've incorporated the idea.
      – Jeff Schaller
      yesterday






    • 2




      command_not_found_handle doesn't work on something like 123/456, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With the ERR trap, I think you could also use $BASH_COMMAND to get the full command line, and not just the last word. That would make expressions with spaces work.
      – ilkkachu
      yesterday






    • 1




      A workaround using readline is possible and seems simpler/easier.
      – Isaac
      14 hours ago


















    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() {
      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
        14 hours ago










      • @Isaac, hmm, that's not good. I'll admit I'd never tested it for speed, though I wonder if it matters here if this is for interactive use. Anyway, as mentioned, this is just another imperfect idea. I originally wasn't going to post it at all, but there are other somewhat hackish Bash solutions here, too, so I think it can stay.
        – ilkkachu
        9 hours ago













      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

























      6 Answers
      6






      active

      oldest

      votes








      6 Answers
      6






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes








      up vote
      15
      down vote



      accepted










      Shortcut Alt-c (bash)



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



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


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



       calc "23 + 46 * 89"


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



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


      a (+) Alias



      We can define an alias:



      alias +='calc #'


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



       + (56 * 23 + 26) / 17


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



      bash



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


      ksh



       calc(){ s=$(history -1 |                          # last command(s)
      sed '$!d;s/^[ t]*[0-9]*[ t]*+ //'); # clean it up
      # (assume one line commads)
      eval 'bc -l <<<"'"$s"'"'; # Do the math.
      }


      zsh zsh doesn't allow neither a + alias nor a # character.



      The value will be printed as:



       $ + (56 * 23 + 26) / 17
      77.29411764705882352941


      Only a + is required, String is quoted (no globs), shell variables accepted:



       $ a=23
      $ + (56 * 23 + $a) / 17
      77.11764705882352941176


      a (+) Function



      With some limitations, this is the closest I got to your request with a function (in bash):



      +() { bc -l <<< "$*"; }


      Which will work like this:



      $ + 25+68+8/24
      93.33333333333333333333


      The problem is that the shell parsing isn't avoided and a * (for example) could get expanded to the list of files in the pwd.



      If you write the command line without (white) spaces you will probably be ok.



      Beware of writing things like $(...) because they will get expanded.



      The safe solution is to quote the string to be evaluated:



      $ + '45 + (58+3 * l(23))/7'
      54.62949752111249272462

      $ + '4 * a(1) * 2'
      6.28318530717958647688


      Which is only two characters shorter that your _bc "6/2", but a + seems more intuitive to me.






      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
        15 hours ago










      • @waldauf Added a readline solution (no + needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
        – Isaac
        15 hours ago










      • @waldauf The name of the function could perfectly be named _calc or anything else.
        – Isaac
        15 hours ago










      • @Isaac Solution with keyboard shortcut looks great! I'll try to do it for bindkey in ZSH. :]
        – waldauf
        13 hours ago










      • @waldauf It seems a bit strange to both pick a convention that has an underscore at the beginning of all names (may require the shift key!) and complain about _bc taking a long time to type :-)
        – ShreevatsaR
        7 hours ago















      up vote
      15
      down vote



      accepted










      Shortcut Alt-c (bash)



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



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


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



       calc "23 + 46 * 89"


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



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


      a (+) Alias



      We can define an alias:



      alias +='calc #'


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



       + (56 * 23 + 26) / 17


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



      bash



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


      ksh



       calc(){ s=$(history -1 |                          # last command(s)
      sed '$!d;s/^[ t]*[0-9]*[ t]*+ //'); # clean it up
      # (assume one line commads)
      eval 'bc -l <<<"'"$s"'"'; # Do the math.
      }


      zsh zsh doesn't allow neither a + alias nor a # character.



      The value will be printed as:



       $ + (56 * 23 + 26) / 17
      77.29411764705882352941


      Only a + is required, String is quoted (no globs), shell variables accepted:



       $ a=23
      $ + (56 * 23 + $a) / 17
      77.11764705882352941176


      a (+) Function



      With some limitations, this is the closest I got to your request with a function (in bash):



      +() { bc -l <<< "$*"; }


      Which will work like this:



      $ + 25+68+8/24
      93.33333333333333333333


      The problem is that the shell parsing isn't avoided and a * (for example) could get expanded to the list of files in the pwd.



      If you write the command line without (white) spaces you will probably be ok.



      Beware of writing things like $(...) because they will get expanded.



      The safe solution is to quote the string to be evaluated:



      $ + '45 + (58+3 * l(23))/7'
      54.62949752111249272462

      $ + '4 * a(1) * 2'
      6.28318530717958647688


      Which is only two characters shorter that your _bc "6/2", but a + seems more intuitive to me.






      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
        15 hours ago










      • @waldauf Added a readline solution (no + needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
        – Isaac
        15 hours ago










      • @waldauf The name of the function could perfectly be named _calc or anything else.
        – Isaac
        15 hours ago










      • @Isaac Solution with keyboard shortcut looks great! I'll try to do it for bindkey in ZSH. :]
        – waldauf
        13 hours ago










      • @waldauf It seems a bit strange to both pick a convention that has an underscore at the beginning of all names (may require the shift key!) and complain about _bc taking a long time to type :-)
        – ShreevatsaR
        7 hours ago













      up vote
      15
      down vote



      accepted







      up vote
      15
      down vote



      accepted






      Shortcut Alt-c (bash)



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



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


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



       calc "23 + 46 * 89"


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



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


      a (+) Alias



      We can define an alias:



      alias +='calc #'


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



       + (56 * 23 + 26) / 17


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



      bash



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


      ksh



       calc(){ s=$(history -1 |                          # last command(s)
      sed '$!d;s/^[ t]*[0-9]*[ t]*+ //'); # clean it up
      # (assume one line commads)
      eval 'bc -l <<<"'"$s"'"'; # Do the math.
      }


      zsh zsh doesn't allow neither a + alias nor a # character.



      The value will be printed as:



       $ + (56 * 23 + 26) / 17
      77.29411764705882352941


      Only a + is required, String is quoted (no globs), shell variables accepted:



       $ a=23
      $ + (56 * 23 + $a) / 17
      77.11764705882352941176


      a (+) Function



      With some limitations, this is the closest I got to your request with a function (in bash):



      +() { bc -l <<< "$*"; }


      Which will work like this:



      $ + 25+68+8/24
      93.33333333333333333333


      The problem is that the shell parsing isn't avoided and a * (for example) could get expanded to the list of files in the pwd.



      If you write the command line without (white) spaces you will probably be ok.



      Beware of writing things like $(...) because they will get expanded.



      The safe solution is to quote the string to be evaluated:



      $ + '45 + (58+3 * l(23))/7'
      54.62949752111249272462

      $ + '4 * a(1) * 2'
      6.28318530717958647688


      Which is only two characters shorter that your _bc "6/2", but a + seems more intuitive to me.






      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 14 hours ago

























      answered yesterday









      Isaac

      10.6k11447




      10.6k11447












      • @Issac Thank you for your answer. Yes, it's shorter - but it's about name of function which you choose. In my own name convention has every function underscore at the beginning of name.
        – waldauf
        15 hours ago










      • @waldauf Added a readline solution (no + needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
        – Isaac
        15 hours ago










      • @waldauf The name of the function could perfectly be named _calc or anything else.
        – Isaac
        15 hours ago










      • @Isaac Solution with keyboard shortcut looks great! I'll try to do it for bindkey in ZSH. :]
        – waldauf
        13 hours ago










      • @waldauf It seems a bit strange to both pick a convention that has an underscore at the beginning of all names (may require the shift key!) and complain about _bc taking a long time to type :-)
        – ShreevatsaR
        7 hours ago


















      • @Issac Thank you for your answer. Yes, it's shorter - but it's about name of function which you choose. In my own name convention has every function underscore at the beginning of name.
        – waldauf
        15 hours ago










      • @waldauf Added a readline solution (no + needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
        – Isaac
        15 hours ago










      • @waldauf The name of the function could perfectly be named _calc or anything else.
        – Isaac
        15 hours ago










      • @Isaac Solution with keyboard shortcut looks great! I'll try to do it for bindkey in ZSH. :]
        – waldauf
        13 hours ago










      • @waldauf It seems a bit strange to both pick a convention that has an underscore at the beginning of all names (may require the shift key!) and complain about _bc taking a long time to type :-)
        – ShreevatsaR
        7 hours ago
















      @Issac Thank you for your answer. Yes, it's shorter - but it's about name of function which you choose. In my own name convention has every function underscore at the beginning of name.
      – waldauf
      15 hours ago




      @Issac Thank you for your answer. Yes, it's shorter - but it's about name of function which you choose. In my own name convention has every function underscore at the beginning of name.
      – waldauf
      15 hours ago












      @waldauf Added a readline solution (no + needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
      – Isaac
      15 hours ago




      @waldauf Added a readline solution (no + needed, only one keyboard chord). Require the calc function. Also an alias solution, a bit more complex.
      – Isaac
      15 hours ago












      @waldauf The name of the function could perfectly be named _calc or anything else.
      – Isaac
      15 hours ago




      @waldauf The name of the function could perfectly be named _calc or anything else.
      – Isaac
      15 hours ago












      @Isaac Solution with keyboard shortcut looks great! I'll try to do it for bindkey in ZSH. :]
      – waldauf
      13 hours ago




      @Isaac Solution with keyboard shortcut looks great! I'll try to do it for bindkey in ZSH. :]
      – waldauf
      13 hours ago












      @waldauf It seems a bit strange to both pick a convention that has an underscore at the beginning of all names (may require the shift key!) and complain about _bc taking a long time to type :-)
      – ShreevatsaR
      7 hours ago




      @waldauf It seems a bit strange to both pick a convention that has an underscore at the beginning of all names (may require the shift key!) and complain about _bc taking a long time to type :-)
      – ShreevatsaR
      7 hours ago












      up vote
      10
      down vote













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



      asis() { bc <<< "$(history 1 | perl -pe 's/^ *[0-9]+ +[^ ]+ //')"; }
      alias c='asis #'


      Then:



      $ c 1+1
      2
      $ c -10 + 20 / 5
      -6
      $ c (-10 + 20) / 5
      2
      $ c 2^8 / 13
      19
      $ c scale=5; 2^8 / 13
      19.69230


      The magic is the fact that alias expansion happens before the usual command line processing, which allows us to create a command whose remaining arguments follow a comment character, that the implementing function finds with the history command.



      This magic allows me to type *, (, and other characters literally. But that also means I can't use shell variables because $ is also literal:



      $ x=5.0
      $ y=-1.2
      $ z=4.7
      $ c ($x + $y) > $z
      (standard_in) 1: illegal character: $
      (standard_in) 1: illegal character: $
      (standard_in) 1: illegal character: $


      I get around this by a bit of bootstrapping:



      $ echo "x=$x; y=$y; z=$z"
      x=5.0; y=-1.2; z=4.7
      $ c x=5.0; y=-1.2; z=4.7; (x + y) > z
      0


      You might just be better off typing: bc Enter 1 + 1 Enter Control+D





      As a side note, I have my default bc settings (like scale) in $HOME/.bc and I use bc -l in the alias. Your use may not require these modifications.






      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
        yesterday












      • @mosvy Well, can't as written in my answer. But certainly eval raises this hack to all-new heights!
        – bishop
        yesterday








      • 1




        +1 Thanks for your idea, I implemented an alternative in my answer. Note that the value of HISTTIMEFORMAT needs to be cleared to avoid the time tag printed by history. Perl seems like too much for this little edition use.
        – Isaac
        14 hours ago















      up vote
      10
      down vote













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



      asis() { bc <<< "$(history 1 | perl -pe 's/^ *[0-9]+ +[^ ]+ //')"; }
      alias c='asis #'


      Then:



      $ c 1+1
      2
      $ c -10 + 20 / 5
      -6
      $ c (-10 + 20) / 5
      2
      $ c 2^8 / 13
      19
      $ c scale=5; 2^8 / 13
      19.69230


      The magic is the fact that alias expansion happens before the usual command line processing, which allows us to create a command whose remaining arguments follow a comment character, that the implementing function finds with the history command.



      This magic allows me to type *, (, and other characters literally. But that also means I can't use shell variables because $ is also literal:



      $ x=5.0
      $ y=-1.2
      $ z=4.7
      $ c ($x + $y) > $z
      (standard_in) 1: illegal character: $
      (standard_in) 1: illegal character: $
      (standard_in) 1: illegal character: $


      I get around this by a bit of bootstrapping:



      $ echo "x=$x; y=$y; z=$z"
      x=5.0; y=-1.2; z=4.7
      $ c x=5.0; y=-1.2; z=4.7; (x + y) > z
      0


      You might just be better off typing: bc Enter 1 + 1 Enter Control+D





      As a side note, I have my default bc settings (like scale) in $HOME/.bc and I use bc -l in the alias. Your use may not require these modifications.






      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
        yesterday












      • @mosvy Well, can't as written in my answer. But certainly eval raises this hack to all-new heights!
        – bishop
        yesterday








      • 1




        +1 Thanks for your idea, I implemented an alternative in my answer. Note that the value of HISTTIMEFORMAT needs to be cleared to avoid the time tag printed by history. Perl seems like too much for this little edition use.
        – Isaac
        14 hours ago













      up vote
      10
      down vote










      up vote
      10
      down vote









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



      asis() { bc <<< "$(history 1 | perl -pe 's/^ *[0-9]+ +[^ ]+ //')"; }
      alias c='asis #'


      Then:



      $ c 1+1
      2
      $ c -10 + 20 / 5
      -6
      $ c (-10 + 20) / 5
      2
      $ c 2^8 / 13
      19
      $ c scale=5; 2^8 / 13
      19.69230


      The magic is the fact that alias expansion happens before the usual command line processing, which allows us to create a command whose remaining arguments follow a comment character, that the implementing function finds with the history command.



      This magic allows me to type *, (, and other characters literally. But that also means I can't use shell variables because $ is also literal:



      $ x=5.0
      $ y=-1.2
      $ z=4.7
      $ c ($x + $y) > $z
      (standard_in) 1: illegal character: $
      (standard_in) 1: illegal character: $
      (standard_in) 1: illegal character: $


      I get around this by a bit of bootstrapping:



      $ echo "x=$x; y=$y; z=$z"
      x=5.0; y=-1.2; z=4.7
      $ c x=5.0; y=-1.2; z=4.7; (x + y) > z
      0


      You might just be better off typing: bc Enter 1 + 1 Enter Control+D





      As a side note, I have my default bc settings (like scale) in $HOME/.bc and I use bc -l in the alias. Your use may not require these modifications.






      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 yesterday

























      answered yesterday









      bishop

      2,0162821




      2,0162821












      • why are you saying that you can't use shell variables? you can! alias c='calc #'; calc(){ eval "$(history -a /dev/stdout | sed 's/c /expr /;s/[()*/+-]/ \& /g')"; }; then x=33; c ($x/3)*10. This has a great potential, thanks for the trick!
        – mosvy
        yesterday












      • @mosvy Well, can't as written in my answer. But certainly eval raises this hack to all-new heights!
        – bishop
        yesterday








      • 1




        +1 Thanks for your idea, I implemented an alternative in my answer. Note that the value of HISTTIMEFORMAT needs to be cleared to avoid the time tag printed by history. Perl seems like too much for this little edition use.
        – Isaac
        14 hours ago


















      • why are you saying that you can't use shell variables? you can! alias c='calc #'; calc(){ eval "$(history -a /dev/stdout | sed 's/c /expr /;s/[()*/+-]/ \& /g')"; }; then x=33; c ($x/3)*10. This has a great potential, thanks for the trick!
        – mosvy
        yesterday












      • @mosvy Well, can't as written in my answer. But certainly eval raises this hack to all-new heights!
        – bishop
        yesterday








      • 1




        +1 Thanks for your idea, I implemented an alternative in my answer. Note that the value of HISTTIMEFORMAT needs to be cleared to avoid the time tag printed by history. Perl seems like too much for this little edition use.
        – Isaac
        14 hours ago
















      why are you saying that you can't use shell variables? you can! alias c='calc #'; calc(){ eval "$(history -a /dev/stdout | sed 's/c /expr /;s/[()*/+-]/ \& /g')"; }; then x=33; c ($x/3)*10. This has a great potential, thanks for the trick!
      – mosvy
      yesterday






      why are you saying that you can't use shell variables? you can! alias c='calc #'; calc(){ eval "$(history -a /dev/stdout | sed 's/c /expr /;s/[()*/+-]/ \& /g')"; }; then x=33; c ($x/3)*10. This has a great potential, thanks for the trick!
      – mosvy
      yesterday














      @mosvy Well, can't as written in my answer. But certainly eval raises this hack to all-new heights!
      – bishop
      yesterday






      @mosvy Well, can't as written in my answer. But certainly eval raises this hack to all-new heights!
      – bishop
      yesterday






      1




      1




      +1 Thanks for your idea, I implemented an alternative in my answer. Note that the value of HISTTIMEFORMAT needs to be cleared to avoid the time tag printed by history. Perl seems like too much for this little edition use.
      – Isaac
      14 hours ago




      +1 Thanks for your idea, I implemented an alternative in my answer. Note that the value of HISTTIMEFORMAT needs to be cleared to avoid the time tag printed by history. Perl seems like too much for this little edition use.
      – Isaac
      14 hours ago










      up vote
      8
      down vote













      In zsh, you could do something like:



      autoload zcalc
      accept-line() {
      if [[ $BUFFER =~ '^[ (]*[+-]? *(0[xX]|.)?[[:digit:]]+[^[:alnum:]]' ]]; then
      echo
      zcalc -e $BUFFER
      print -rs -- $BUFFER
      BUFFER=
      fi
      zle .$WIDGET
      }
      zle -N accept-line


      It redefines the accept-line widget (mapped on Enter) to a user-defined widget that checks if the current line starts with a number (decimal or hexadecimal) optionally prefixed with any number of (s, looking for a non-alnum character after that to avoid false positives for commands like 7zip or 411toppm.



      If that matches then we pass it to zcalc (more useful than bc in that it can use shell variables and all of zsh math functions and number styles, but does not support arbitrary precision), add the line to history and accept an empty command.



      Note that it can cause confusion if you enter a line with digits in things like:



      cat << EOF
      213 whatever
      EOF


      Or:



      var=(
      123 456
      )





      share|improve this answer



























        up vote
        8
        down vote













        In zsh, you could do something like:



        autoload zcalc
        accept-line() {
        if [[ $BUFFER =~ '^[ (]*[+-]? *(0[xX]|.)?[[:digit:]]+[^[:alnum:]]' ]]; then
        echo
        zcalc -e $BUFFER
        print -rs -- $BUFFER
        BUFFER=
        fi
        zle .$WIDGET
        }
        zle -N accept-line


        It redefines the accept-line widget (mapped on Enter) to a user-defined widget that checks if the current line starts with a number (decimal or hexadecimal) optionally prefixed with any number of (s, looking for a non-alnum character after that to avoid false positives for commands like 7zip or 411toppm.



        If that matches then we pass it to zcalc (more useful than bc in that it can use shell variables and all of zsh math functions and number styles, but does not support arbitrary precision), add the line to history and accept an empty command.



        Note that it can cause confusion if you enter a line with digits in things like:



        cat << EOF
        213 whatever
        EOF


        Or:



        var=(
        123 456
        )





        share|improve this answer

























          up vote
          8
          down vote










          up vote
          8
          down vote









          In zsh, you could do something like:



          autoload zcalc
          accept-line() {
          if [[ $BUFFER =~ '^[ (]*[+-]? *(0[xX]|.)?[[:digit:]]+[^[:alnum:]]' ]]; then
          echo
          zcalc -e $BUFFER
          print -rs -- $BUFFER
          BUFFER=
          fi
          zle .$WIDGET
          }
          zle -N accept-line


          It redefines the accept-line widget (mapped on Enter) to a user-defined widget that checks if the current line starts with a number (decimal or hexadecimal) optionally prefixed with any number of (s, looking for a non-alnum character after that to avoid false positives for commands like 7zip or 411toppm.



          If that matches then we pass it to zcalc (more useful than bc in that it can use shell variables and all of zsh math functions and number styles, but does not support arbitrary precision), add the line to history and accept an empty command.



          Note that it can cause confusion if you enter a line with digits in things like:



          cat << EOF
          213 whatever
          EOF


          Or:



          var=(
          123 456
          )





          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 yesterday

























          answered yesterday









          Stéphane Chazelas

          296k54559904




          296k54559904






















              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
                yesterday






              • 2




                bash and zsh have a command-not-found-handler hook (mispelled handle in bash) that you could also use here.
                – Stéphane Chazelas
                yesterday












              • Thanks, Stéphane! I've incorporated the idea.
                – Jeff Schaller
                yesterday






              • 2




                command_not_found_handle doesn't work on something like 123/456, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With the ERR trap, I think you could also use $BASH_COMMAND to get the full command line, and not just the last word. That would make expressions with spaces work.
                – ilkkachu
                yesterday






              • 1




                A workaround using readline is possible and seems simpler/easier.
                – Isaac
                14 hours ago















              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
                yesterday






              • 2




                bash and zsh have a command-not-found-handler hook (mispelled handle in bash) that you could also use here.
                – Stéphane Chazelas
                yesterday












              • Thanks, Stéphane! I've incorporated the idea.
                – Jeff Schaller
                yesterday






              • 2




                command_not_found_handle doesn't work on something like 123/456, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With the ERR trap, I think you could also use $BASH_COMMAND to get the full command line, and not just the last word. That would make expressions with spaces work.
                – ilkkachu
                yesterday






              • 1




                A workaround using readline is possible and seems simpler/easier.
                – Isaac
                14 hours ago













              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 yesterday

























              answered yesterday









              Jeff Schaller

              37.3k1052121




              37.3k1052121












              • Caveat - the ERR trap is not inherited to subshells, so (1+2) would need to be '(1+2)'
                – Jeff Schaller
                yesterday






              • 2




                bash and zsh have a command-not-found-handler hook (mispelled handle in bash) that you could also use here.
                – Stéphane Chazelas
                yesterday












              • Thanks, Stéphane! I've incorporated the idea.
                – Jeff Schaller
                yesterday






              • 2




                command_not_found_handle doesn't work on something like 123/456, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With the ERR trap, I think you could also use $BASH_COMMAND to get the full command line, and not just the last word. That would make expressions with spaces work.
                – ilkkachu
                yesterday






              • 1




                A workaround using readline is possible and seems simpler/easier.
                – Isaac
                14 hours ago


















              • Caveat - the ERR trap is not inherited to subshells, so (1+2) would need to be '(1+2)'
                – Jeff Schaller
                yesterday






              • 2




                bash and zsh have a command-not-found-handler hook (mispelled handle in bash) that you could also use here.
                – Stéphane Chazelas
                yesterday












              • Thanks, Stéphane! I've incorporated the idea.
                – Jeff Schaller
                yesterday






              • 2




                command_not_found_handle doesn't work on something like 123/456, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With the ERR trap, I think you could also use $BASH_COMMAND to get the full command line, and not just the last word. That would make expressions with spaces work.
                – ilkkachu
                yesterday






              • 1




                A workaround using readline is possible and seems simpler/easier.
                – Isaac
                14 hours ago
















              Caveat - the ERR trap is not inherited to subshells, so (1+2) would need to be '(1+2)'
              – Jeff Schaller
              yesterday




              Caveat - the ERR trap is not inherited to subshells, so (1+2) would need to be '(1+2)'
              – Jeff Schaller
              yesterday




              2




              2




              bash and zsh have a command-not-found-handler hook (mispelled handle in bash) that you could also use here.
              – Stéphane Chazelas
              yesterday






              bash and zsh have a command-not-found-handler hook (mispelled handle in bash) that you could also use here.
              – Stéphane Chazelas
              yesterday














              Thanks, Stéphane! I've incorporated the idea.
              – Jeff Schaller
              yesterday




              Thanks, Stéphane! I've incorporated the idea.
              – Jeff Schaller
              yesterday




              2




              2




              command_not_found_handle doesn't work on something like 123/456, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With the ERR trap, I think you could also use $BASH_COMMAND to get the full command line, and not just the last word. That would make expressions with spaces work.
              – ilkkachu
              yesterday




              command_not_found_handle doesn't work on something like 123/456, though. Bash doesn't seem to bother calling the hook on commands that look like pathnames. With the ERR trap, I think you could also use $BASH_COMMAND to get the full command line, and not just the last word. That would make expressions with spaces work.
              – ilkkachu
              yesterday




              1




              1




              A workaround using readline is possible and seems simpler/easier.
              – Isaac
              14 hours ago




              A workaround using readline is possible and seems simpler/easier.
              – Isaac
              14 hours ago










              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 yesterday









                  sudodus

                  61116




                  61116






















                      up vote
                      1
                      down vote













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



                      $ cat bash_calc.sh
                      shopt -s extdebug
                      debug_calc() {
                      local re='^[ (]*-?[0-9]'
                      if [[ $BASH_COMMAND =~ $re ]]; then
                      echo "$BASH_COMMAND" | bc -l
                      return 1
                      fi
                      }
                      trap debug_calc DEBUG
                      $ . ./bash_calc.sh
                      $ 123 * 456
                      56088
                      $ 123/456
                      .26973684210526315789


                      The trap gets the full command line before expanding variables or filename patterns, so an unquoted * works. (But using shell variables in the calculation doesn't work.)



                      However, unquoted parenthesis still cause a syntax error, so this is not perfect either.



                      (I nicked the regex above from Stéphane's answer.)






                      share|improve this answer























                      • Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
                        – Isaac
                        14 hours ago










                      • @Isaac, hmm, that's not good. I'll admit I'd never tested it for speed, though I wonder if it matters here if this is for interactive use. Anyway, as mentioned, this is just another imperfect idea. I originally wasn't going to post it at all, but there are other somewhat hackish Bash solutions here, too, so I think it can stay.
                        – ilkkachu
                        9 hours ago

















                      up vote
                      1
                      down vote













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



                      $ cat bash_calc.sh
                      shopt -s extdebug
                      debug_calc() {
                      local re='^[ (]*-?[0-9]'
                      if [[ $BASH_COMMAND =~ $re ]]; then
                      echo "$BASH_COMMAND" | bc -l
                      return 1
                      fi
                      }
                      trap debug_calc DEBUG
                      $ . ./bash_calc.sh
                      $ 123 * 456
                      56088
                      $ 123/456
                      .26973684210526315789


                      The trap gets the full command line before expanding variables or filename patterns, so an unquoted * works. (But using shell variables in the calculation doesn't work.)



                      However, unquoted parenthesis still cause a syntax error, so this is not perfect either.



                      (I nicked the regex above from Stéphane's answer.)






                      share|improve this answer























                      • Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
                        – Isaac
                        14 hours ago










                      • @Isaac, hmm, that's not good. I'll admit I'd never tested it for speed, though I wonder if it matters here if this is for interactive use. Anyway, as mentioned, this is just another imperfect idea. I originally wasn't going to post it at all, but there are other somewhat hackish Bash solutions here, too, so I think it can stay.
                        – ilkkachu
                        9 hours ago















                      up vote
                      1
                      down vote










                      up vote
                      1
                      down vote









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



                      $ cat bash_calc.sh
                      shopt -s extdebug
                      debug_calc() {
                      local re='^[ (]*-?[0-9]'
                      if [[ $BASH_COMMAND =~ $re ]]; then
                      echo "$BASH_COMMAND" | bc -l
                      return 1
                      fi
                      }
                      trap debug_calc DEBUG
                      $ . ./bash_calc.sh
                      $ 123 * 456
                      56088
                      $ 123/456
                      .26973684210526315789


                      The trap gets the full command line before expanding variables or filename patterns, so an unquoted * works. (But using shell variables in the calculation doesn't work.)



                      However, unquoted parenthesis still cause a syntax error, so this is not perfect either.



                      (I nicked the regex above from Stéphane's answer.)






                      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 yesterday

























                      answered yesterday









                      ilkkachu

                      54.2k782147




                      54.2k782147












                      • Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
                        – Isaac
                        14 hours ago










                      • @Isaac, hmm, that's not good. I'll admit I'd never tested it for speed, though I wonder if it matters here if this is for interactive use. Anyway, as mentioned, this is just another imperfect idea. I originally wasn't going to post it at all, but there are other somewhat hackish Bash solutions here, too, so I think it can stay.
                        – ilkkachu
                        9 hours ago




















                      • Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
                        – Isaac
                        14 hours ago










                      • @Isaac, hmm, that's not good. I'll admit I'd never tested it for speed, though I wonder if it matters here if this is for interactive use. Anyway, as mentioned, this is just another imperfect idea. I originally wasn't going to post it at all, but there are other somewhat hackish Bash solutions here, too, so I think it can stay.
                        – ilkkachu
                        9 hours ago


















                      Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
                      – Isaac
                      14 hours ago




                      Running with extdebug active will make the shell quite slower, but it is a clever idea, thanks.
                      – Isaac
                      14 hours ago












                      @Isaac, hmm, that's not good. I'll admit I'd never tested it for speed, though I wonder if it matters here if this is for interactive use. Anyway, as mentioned, this is just another imperfect idea. I originally wasn't going to post it at all, but there are other somewhat hackish Bash solutions here, too, so I think it can stay.
                      – ilkkachu
                      9 hours ago






                      @Isaac, hmm, that's not good. I'll admit I'd never tested it for speed, though I wonder if it matters here if this is for interactive use. Anyway, as mentioned, this is just another imperfect idea. I originally wasn't going to post it at all, but there are other somewhat hackish Bash solutions here, too, so I think it can stay.
                      – ilkkachu
                      9 hours ago




















                      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

                      List directoties down one level, excluding some named directories and files

                      list processes belonging to a network namespace

                      list systemd RuntimeDirectory mounts