在這篇文章里,我們將會探索如何使用Python語言作為一個工具來檢測Linux系統各種運行信息。讓我們一起來學習吧。
哪種Python?
當我提到Python時,我一般是指CPython 2(準確來說是2.7)。當同樣的代碼不能在CPython3(3.3)運行時,我們明確地把它指出并給出替代的代碼,解釋它們之間的不同點。請確保你已經安裝了CPython,在終端輸入python或者python3你會看到Python提示符出現在你的終端里。
請注意,所有的腳本程序都會以#!/usr/bin/env python作為第一行,意味著我們要Python解析器去運行這些腳本。因此,如果你使用 chmod +x your-script.py 命令給你的腳本添加可執行的權限,你可以使用./your-script.py命令直接運行你的腳本(你將會在這篇文章里看到這種操作)
探索platform模塊
在標準庫中的platform模塊有大量的函數讓我們去檢查各種系統信息。我們一起來打開Python解釋器(譯者:直接在命令行輸入python即可打開)并探索其中的一部分函數。我們先從platform.uname()函數開始:
>>> import platform>>> platform.uname()('Linux', 'fedora.echorand', '3.7.4-204.fc18.x86_64', '#1 SMP Wed Jan 23 16:44:29 UTC 2013', 'x86_64')如果你知道Linux上的uname命令,你會意識到這個函數就是uname命令的一個接口。在Python 2,這個函數會返回一個由系統類型(或者內核類型),主機名,版本號,發行版號,主機硬件架構和處理器類型組成的元組。你可以使用索引來獲取單個屬性,像這樣:
>>> platform.uname()[0]'Linux'
在Python 3,這個函數會返回一個默認命名的元組:
>>> platform.uname() uname_result(system='Linux', node='fedora.echorand',release='3.7.4-204.fc18.x86_64', version='#1 SMP Wed Jan 23 16:44:29UTC 2013', machine='x86_64', processor='x86_64')
因為返回值是個默認命名的元組,所以我們可以輕易地通過變量名來獲取單個屬性而不用去記住各個屬性的下標,像這樣:
>>> platform.uname().system'Linux'
platfrom模塊還提供了一些直接的接口來獲取上面的屬性值,像這些:
>>> platform.system()'Linux' >>> platform.release()'3.7.4-204.fc18.x86_64'
函數linx_distribution()返回你正在使用的Linux發行版的詳細信息。舉個例子,在Fedora 18系統中,這條命令會返回下面的信息:
>>> platform.linux_distribution()('Fedora', '18', 'Spherical Cow')返回值是一個由發行版本名,版本號,代號組成的元組。你可以通過_supported_dists屬性來打印你所用的Python版本支持哪些發行版本:
>>> platform._supported_dists('SuSE', 'debian', 'fedora', 'redhat', 'centos', 'mandrake','mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo','UnitedLinux', 'turbolinux')如果你的Linux發行版本不是上面那些的其中一個(或者是其中一個的衍生版),那么你調用上面的函數時不會看到任何有用的信息。
最后一個我們要探索的platfrom函數是architecture()函數。當你不添加任何的參數來調用這個函數時,這個函數會返回一個由位架構和Python可執行文件格式組成的元組。比如:
>>> platform.architecture()('64bit', 'ELF')在32位的Linux系統中,你會看到:
>>> platform.architecture()('32bit', 'ELF')如果你指定其他任意的系統可執行程序作為參數,你會得到類似的結果:
>>> platform.architecture(executable='/usr/bin/ls')('64bit', 'ELF')我們鼓勵你去探索platfrom模塊中的其他函數,讓你找到你當前使用的Python的版本。如果你非常想知道這個模塊是怎樣獲取這些信息的,你可以去看Python源代碼目錄下的Lib/platfrom.py文件。
os和sys模塊同樣是一個獲取統屬性的有用模塊就像本地BYTEORDER一樣。下一步,我們將會不使用Python你標準庫模塊來探索一些獲取Linux系統信息的通用方法,這次我們通過proc和sys文件系統來實現。要注意的是,通過這些文件系統獲取的信息在不同的硬件架構里會有所不同。因此,在閱讀這篇文章和寫腳本從這些文件里獲取系統信息時要它記住。
CPU信息
/proc/cpuinfo這個文件包含了你系統的處理單元信息。比如,這里有一個與在命令行輸入cat /proc/cpuinfo 具備同樣功能的Python腳本
#! /usr/bin/env python""" print out the /proc/cpuinfo file""" from __future__ import print_function with open('/proc/cpuinfo') as f: for line in f: print(line.rstrip('n'))當你用Python 2或者Python 3運行這個腳本時,你會看到/proc/cpuinfo文件的所有內容都在你的屏幕上顯示出來。(在上面這個腳本,rstrip()方法把每一行的換行符去掉)
下個代碼清單使用了startwith()這個字符串方法來顯示你電腦的處理單元型號
#! /usr/bin/env python """ Print the model of your processing units """ from __future__ import print_function with open('/proc/cpuinfo') as f: for line in f: # Ignore the blank line separating the information between # details about two processing units if line.strip(): if line.rstrip('n').startswith('model name'): model_name = line.rstrip('n').split(':')[1] print(model_name)當你運行這個腳本,你會看到你機器的所有處理單元的型號。比如,下面是我在我計算機里看到的:
Intel(R) Core(TM) i7-3520M CPU @ 2.90GHzIntel(R) Core(TM) i7-3520M CPU @ 2.90GHzIntel(R) Core(TM) i7-3520M CPU @ 2.90GHzIntel(R) Core(TM) i7-3520M CPU @ 2.90GHz
到目前為止我們已經有好幾種方法用來獲取我們的計算機系統構架了。從技術的角度準確地說,所有的這些方法實際上是呈現了你運行的系統內核的架構。因此,如果你計算機實際上是64位的機器,但是運行著32位的內核,那么上面的方法將會顯現你的計算機是32位架構的。為了找出計算機的正確架構,你可以查看在/proc/cpuinfo中的屬性列表的lm屬性。1m屬性代表著長模式(Long mode)并且只會在64位架構的計算機上出現。下面的腳本跟你展示是怎樣做的:
#! /usr/bin/env python """ Find the real bit architecture""" from __future__ import print_function with open('/proc/cpuinfo') as f: for line in f: # Ignore the blank line separating the information between # details about two processing units if line.strip(): if line.rstrip('n').startswith('flags') or line.rstrip('n').startswith('Features'): if 'lm' in line.rstrip('n').split(): print('64-bit') else: print('32-bit')正如目前我們所看到的,我們能夠訪問/proc/cpuinfo文件并且使用簡單的文本處理技術去讀取我們在查找的信息。為了友好地提供數據給其他程序使用,最好的方法可能是把從/proc/cpuinfo里獲取的內容轉換為標準的數據機構,比如轉換為字典類型。方法很簡單:如果你看了這個文件,你會發現對于每一個處理單元都是以鍵值對形式存在(在之前的一個例子中,我們打印的處理器機型名時,這里的model name就是一個鍵。)每個不同處理器單元的信息都會用空行來分開。這使我們能方便地以每個處理單元數據為鍵來構建字典數據結構。這些鍵(key)都有一個值(value),每個值又對應著每一個處理單元在/proc/cupinfo文件中的所有信息。下一個代碼清單告訴你怎樣做:
#!/usr/bin/env/ python """/proc/cpuinfo as a Python dict"""from __future__ import print_functionfrom collections import OrderedDictimport pprint def cpuinfo(): ''' Return the information in /proc/cpuinfo as a dictionary in the following format: cpu_info['proc0']={...} cpu_info['proc1']={...} ''' cpuinfo=OrderedDict() procinfo=OrderedDict() nprocs = 0 with open('/proc/cpuinfo') as f: for line in f: if not line.strip(): # end of one processor cpuinfo['proc%s' % nprocs] = procinfo nprocs=nprocs+1 # Reset procinfo=OrderedDict() else: if len(line.split(':')) == 2: procinfo[line.split(':')[0].strip()] = line.split(':')[1].strip() else: procinfo[line.split(':')[0].strip()] = '' return cpuinfo if __name__=='__main__': cpuinfo = cpuinfo() for processor in cpuinfo.keys(): print(cpuinfo[processor]['model name'])這段代碼使用了一個OrderedDict(有序的字典)代替常用的字典類型,目的是先對在文件中找到的鍵值對排序后再保存。因此,先展示第一個處理單元的數據信息其次是第二個,以此類推。如果你調用這個函數,它會返回一個字典類型給你。字典的每一個鍵都是一個處理單元。然后你可以使用鍵來篩選要找的信息(就像if __name='__main__'語句塊里展示的一樣)。當上面的腳本運行時會再次打印出每個處理單元的model name(通過print(cpuinfo[processor]['model name']語句來展示)
Intel(R) Core(TM) i7-3520M CPU @ 2.90GHzIntel(R) Core(TM) i7-3520M CPU @ 2.90GHzIntel(R) Core(TM) i7-3520M CPU @ 2.90GHzIntel(R) Core(TM) i7-3520M CPU @ 2.90GHz
內存信息
與/proc/cpuinfo類似,/proc/meminfo文件包含了你計算機的主存信息。下一個腳本產生一個包含這個文件內容的字典并把它輸出。
#!/usr/bin/env python from __future__ import print_functionfrom collections import OrderedDict def meminfo(): ''' Return the information in /proc/meminfo as a dictionary ''' meminfo=OrderedDict() with open('/proc/meminfo') as f: for line in f: meminfo[line.split(':')[0]] = line.split(':')[1].strip() return meminfo if __name__=='__main__': #print(meminfo()) meminfo = meminfo() print('Total memory: {0}'.format(meminfo['MemTotal'])) print('Free memory: {0}'.format(meminfo['MemFree']))和前面看到的一樣,你同樣可以使用特定的鍵去獲取任意你想要的信息(在if __name__=='__main__'語句快里有展示)。當你運行這個腳本,你可以看到類似下面的輸出:
Total memory: 7897012 kBFree memory: 249508 kB
網絡統計
下面,我們會探索我們計算機系統的網絡設備。我們將會檢索系統的網絡接口和系統開啟后發送和接收到的字節數據。這些信息可以在/proc/net/dev文件中獲取。如果你審查過這個文件的內容,你會發現前兩行包含了頭信息-i.e.文件中的第一列是網絡接口名,第二和第三列展示了接收和傳輸的字節信息(比如,總發送字節,數據包數量,錯誤統計,等等)。我們感興趣的是如何獲取不同的網絡設備的總數據發送和接受量。下一個代碼清單展示了我們如何從/proc/net/dev提出這些信息:
#!/usr/bin/env pythonfrom __future__ import print_functionfrom collections import namedtuple def netdevs(): ''' RX and TX bytes for each of the network devices ''' with open('/proc/net/dev') as f: net_dump = f.readlines() device_data={} data = namedtuple('data',['rx','tx']) for line in net_dump[2:]: line = line.split(':') if line[0].strip() != 'lo': device_data[line[0].strip()] = data(float(line[1].split()[0])/(1024.0*1024.0), float(line[1].split()[8])/(1024.0*1024.0)) return device_data if __name__=='__main__': netdevs = netdevs() for dev in netdevs.keys(): print('{0}: {1} MiB {2} MiB'.format(dev, netdevs[dev].rx, netdevs[dev].tx))當你運行上面的腳本時,會以MiB為單位輸出從你最近的一次重啟后你的網絡設備接受和發送的數據。正如下面展示的:
em1: 0.0 MiB 0.0 MiBwlan0: 2651.40951061 MiB 183.173976898 MiB
你可能會利用一個持久性存儲機制和這個腳本來寫一個你自己的數據使用監控程序。
進程
/proc目錄同樣包含了每個運行進程的目錄。這些目錄的名稱以相應的進程ID來命名。因此,如果你遍歷/proc目錄下的所有以數字命名的目錄,你會得到一個所有當前運行進程的ID列表。下面的代碼清單里的process_list()函數返回一個包含所有當前運行進程ID的列表。這個列表的長度等于系統運行進程的總數,正如你運行這個腳本看到的一樣:
#!/usr/bin/env python""" List of all process IDs currently active""" from __future__ import print_functionimport osdef process_list(): pids = [] for subdir in os.listdir('/proc'): if subdir.isdigit(): pids.append(subdir) return pids if __name__=='__main__': pids = process_list() print('Total number of running processes:: {0}'.format(len(pids)))運行上面的腳本時,輸出結果和下面輸出類似:
每個進程目錄都包含了大量的其他文件和目錄,這些目錄包含了各種關于進程調用命令,使用的共享庫和其他的信息。
塊設備
接下來的腳本通過訪問sysfs虛擬文件系統列出了所有的塊設備信息。你能夠在/sys/block目錄下找到系統上的所有塊設備。因此,你的系統上會有/sys/block/sda,/sys/block/sdb和其他的類似目錄。為了找到這些設備,我們可以遍歷/sys/block目錄然后通過簡單的正則表達式去匹配我們要查找的內容。
#!/usr/bin/env python """Read block device data from sysfs""" from __future__ import print_functionimport globimport reimport os # Add any other device pattern to read fromdev_pattern = ['sd.*','mmcblk*'] def size(device): nr_sectors = open(device+'/size').read().rstrip('n') sect_size = open(device+'/queue/hw_sector_size').read().rstrip('n') # The sect_size is in bytes, so we convert it to GiB and then send it back return (float(nr_sectors)*float(sect_size))/(1024.0*1024.0*1024.0) def detect_devs(): for device in glob.glob('/sys/block/*'): for pattern in dev_pattern: if re.compile(pattern).match(os.path.basename(device)): print('Device:: {0}, Size:: {1} GiB'.format(device, size(device))) if __name__=='__main__': detect_devs()如果你運行了這個腳本,你將會看到與下面類似的輸出結果:
Device:: /sys/block/sda, Size:: 465.761741638 GiBDevice:: /sys/block/mmcblk0, Size:: 3.70703125 GiB
當我運行這個腳本時,我額外插入了一張SD卡。所以你會看到這個腳本檢測到了它(上面輸出的第二行,譯者注)。你同樣可以擴展這個腳本去識別其他的塊設備(比如虛擬硬盤)。
構建命令行工具
允許用戶指定命令行參數去自定義程序的默認行為是所有Linux命令行工具的一個普遍特征。argparse模塊能使你程序擁有與內置工具界面類似的界面。下一個代碼清單展示了一個獲取你系統上所有用戶并把它們相應的登陸shell打印出來的程序。
#!/usr/bin/env python """Print all the users and their login shells""" from __future__ import print_functionimport pwd # Get the users from /etc/passwddef getusers(): users = pwd.getpwall() for user in users: print('{0}:{1}'.format(user.pw_name, user.pw_shell)) if __name__=='__main__': getusers()當運行上面的腳本時,它會打印出你系統上所有的用戶和它們的登陸shell
現在,我們假設你想讓腳本使用者能夠選擇是否想看到系統的其他用戶(比如daemon,apache)。我們通過使用argparse模塊擴展之前的代碼來實現這個功能,就像下面的代碼。
#!/usr/bin/env python """Utility to play around with users and passwords on a Linux system""" from __future__ import print_functionimport pwdimport argparseimport os def read_login_defs(): uid_min = None uid_max = None if os.path.exists('/etc/login.defs'): with open('/etc/login.defs') as f: login_data = f.readlines() for line in login_data: if line.startswith('UID_MIN'): uid_min = int(line.split()[1].strip()) if line.startswith('UID_MAX'): uid_max = int(line.split()[1].strip()) return uid_min, uid_max # Get the users from /etc/passwddef getusers(no_system=False): uid_min, uid_max = read_login_defs() if uid_min is None: uid_min = 1000 if uid_max is None: uid_max = 60000 users = pwd.getpwall() for user in users: if no_system: if user.pw_uid >= uid_min and user.pw_uid <= uid_max: print('{0}:{1}'.format(user.pw_name, user.pw_shell)) else: print('{0}:{1}'.format(user.pw_name, user.pw_shell)) if __name__=='__main__': parser = argparse.ArgumentParser(description='User/Password Utility') parser.add_argument('--no-system', action='store_true',dest='no_system', default = False, help='Specify to omit system users') args = parser.parse_args() getusers(args.no_system)使用 主站蜘蛛池模板: 娱乐| 黄陵县| 南昌县| 黔西| 尚志市| 铁岭县| 米易县| 天镇县| 海口市| 西宁市| 佳木斯市| 潜江市| 庆元县| 自治县| 法库县| 涞源县| 邯郸县| 中西区| 乌拉特前旗| 桃江县| 遂川县| 渭源县| 乌鲁木齐市| 红安县| 湖州市| 墨竹工卡县| 甘孜| 阿拉善右旗| 阳西县| 黑山县| 灵丘县| 平湖市| 乐清市| 屯留县| 包头市| 东兰县| 西藏| 慈溪市| 岳普湖县| 莫力| 五常市|