1. 程式人生 > >一步步打造自己的linux命令列計算器

一步步打造自己的linux命令列計算器

相信很多人,在工作中會需要使用到計算器。一般的做法是,開啟並使用系統自帶的計算器。

這種做法可能對我來說,有如下幾個問題。

  • 太慢。每次需要開啟計算器,然後改成程式設計模式,手工選擇進位制,再使用輸入表示式進行計算。

  • 需要切換視窗。程式設計時經常是在終端中,使用GUI計算器則意味著要離開終端,計算完畢再切換回來。

  • 無法使用混合進製表達式。混合進位制的意思是,在一個表示式中同時使用多種進位制,如“0x10 * 10”表示十六進位制的0x10乘以十進位制的10。

如果以上有一條你也有同感的話,那麼你也應該試一下,使用命令列計算器。

命令列計算器,呼叫bc

只需經過簡單的搜尋,便可以瞭解到,linux中原生提供了一個命令列計算器 GNU bc。

GNU bc支援高精度數字和多種數值型別(例如二進位制、十進位制、十六進位制)的輸入輸出。

bc的互動式使用方式,執行bc,進入互動模式。在互動模式中輸入表示式,回車即可獲得結果。需要退出時輸入quit退出即可。

bc的非互動式使用方式,通過管道將表示式傳入。

使用效果如下

[email protected]:~$ bc
bc 1.07.1
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
1+2
3
quit
[email protected]
:~$ echo "1+2" | bc 3

OK,get到了命令列計算器的新技能了,但每次進入互動模式或者手工輸入“echo 表示式 | bc ”都感覺略麻煩。那這個時候,就需要指令碼,寫個mycalc.sh好了

[email protected]:~$ cat mycalc.sh
#!/bin/bash

echo "[email protected]" | bc
[email protected]:~$ ./mycalc.sh 1+2
3

再把mycalc.sh拷貝到可訪問的目錄下,如

sudo mv mycalc.sh /usr/bin

對於沒有sudo許可權的情況,那也可以變通下

mkdir -p ~/usr/bin

mv mycalc.sh ~/usr/bin

echo 'export PATH=$HOME/usr/bin:$PATH' >> ~/.bashrc

source ~/.bashrc

再alias一個順手的命令名,比如拼音jisuan

 echo "alias jisuan='mycalc.sh'" >> ~/.bashrc

更多bc的用法,可以通過man bc檢視,網上也有許多介紹資料。

解決進位制問題

bc仍然需要手工指定進位制,在表示式前,使用ibase引數和obase引數指定輸入輸出的進位制。並且不支援混合進位制,因為ibase每次只能指定一種進位制。

[email protected]:~$ echo "10+10" | bc
20
[email protected]:~$ echo "ibase=16;10+10" | bc
32

但我們既然已經有了一個包裝指令碼mycalc.sh,那是不是可以把進位制轉換的工作交給它呢,當然可以。

我們可以讓mycalc.sh先處理下表達式中的數字,約定0x開頭為十六進位制,不帶字首為十進位制,0o開頭為八進位制,0b開頭為二進位制。

mycalc先將所有引數轉換成統一的進位制,如十進位制,然後計算表示式的值,最終將結果再以多種進位制的形式輸出。這樣我們就不同手工處理進位制問題了。

至於輸出,為了方便起見,可以多種進位制一起輸出,需要哪個用哪個即可

這裡就不貼程式碼了,有興趣可移步github https://github.com/zqb-all/smartbc,我們接著往下看,後面有更簡單的方式。

使用示例

[email protected]:~$ type jisuan
jisuan 是 `~/mywork/mygithub/smartbc/smartbc' 的別名
[email protected]:~$ jisuan 10+10
Original EQUATION: 10+10 
Decimal  EQUATION: 10+10
base2 : 10100
base8 : 24
base10: 20
base16: 14
[email protected]:~$ jisuan 10+0x10
Original EQUATION: 10+0x10 
Decimal  EQUATION: 10+16
base2 : 11010
base8 : 32
base10: 26
base16: 1A

更好的實現,使用python

以上基於bc的計算器,已經可以滿足我的需求了,也使用了一段時間。但其實還有更好的實現方式,使用python。

在命令列中,輸入python,進入互動模式,即可像bc一樣執行表示式,得到結果。更棒的是,原生支援混合進位制,不需要自己寫程式碼預處理表達式了。簡單可靠。

程式碼及使用示例

[email protected]:~$ type jisuan
jisuan 是 `~/.pycalc.py' 的別名
[email protected]:~$ cat ~/.pycalc.py 
#!/usr/bin/env python2

import sys

equation=sys.argv[1]
result=eval(equation)
if isinstance(result, (float)):
    print "Attention:only base10 is float, others change to int before type"
print "equation:",sys.argv[1]
print "base2 : ",str(bin(int(result)))
print "base8 : ",str(oct(int(result)))
print "base10: ",str((result))
print "base16: ",str(hex(int(result)))
[email protected]:~$ jisuan 10+10
equation: 10+10
base2 :  0b10100
base8 :  024
base10:  20
base16:  0x14
[email protected]:~$ jisuan 10+0x10
equation: 10+0x10
base2 :  0b11010
base8 :  032
base10:  26
base16:  0x1a

更多輸出格式

一般,輸出十六進位制,十進位制,二進位制三種結果就足夠用了。但如果有特殊需求,也可自己拓展。

比如,當需要核對暫存器,檢查某個bit時,一個個去數二進位制的第19位,是很費眼睛的一件事。

這個時候就需要更加直觀的輸出,可以一眼看到某個bit是0還是1。

那好辦,給二進位制加上下標好了。如下

程式碼

#!/usr/bin/env python2

import sys

def formatBinString(num):
    result='bit:   '
    result_index='index: '
    num_len=len(num)
    if num_len > 32:
        return ""
    for i in num:
        num_len-=1
        result+=i
        result+=' | '
        result_index+=str(num_len).zfill(2)
        result_index+='| '
    return result+'\n'+result_index

equation=sys.argv[1]
result=eval(equation)
if isinstance(result, (float)):
    print "Attention:only base10 is float, others change to int before type"

print "equation:",sys.argv[1]
print ""

print "base2 : ",str(bin(int(result)))
print "base8 : ",str(oct(int(result)))
print "base10: ",str((result))
print "base16: ",str(hex(int(result)))

print ""
print formatBinString(str(bin(int(result))[2:].zfill(32)))

效果

[email protected]:~$ jisuan 10+0x10
equation: 10+0x10

base2 :  0b11010
base8 :  032
base10:  26
base16:  0x1a

bit:   0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 
index: 31| 30| 29| 28| 27| 26| 25| 24| 23| 22| 21| 20| 19| 18| 17| 16| 15| 14| 13| 12| 11| 10| 09| 08| 07| 06| 05| 04| 03| 02| 01| 00|