이 기사에서는 Python 프로그래밍 언어 도구를 사용하여 Linux 시스템에서 다양한 정보를 검색하는 방법을 살펴보겠습니다. 갑시다.
Python을 언급할 때 CPython 2(정확히는 2.7)를 언급합니다. 동일한 코드가 CPython 3(3.3)에서는 작동하지 않는다는 점을 명시적으로 언급하고 차이점에 대한 백업 설명을 제공합니다. . CPython을 설치했는지 확인하고 터미널에 python 또는 python3을 입력한 후 Enter 키를 누르면 터미널에 Python 프롬프트(프롬프트)가 표시됩니다.
모든 프로그램의 첫 번째 줄에는 #!/usr/bin/env/python이 있습니다. 즉, Python 인터프리터가 이러한 스크립트를 실행하기를 원합니다. 따라서 스크립트를 실행 가능하게 하려면 chmod +x your-script.py를 사용하고 ./your-script.py를 사용하여 실행할 수 있습니다(이 기사에서 이 내용을 볼 수 있습니다)
플랫폼 모듈은 표준 라이브러리에 있으며 수많은 시스템 정보를 얻을 수 있는 많은 기능을 가지고 있습니다. 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 명령을 이미 알고 있다면 이 기능이 이 명령에 대한 인터페이스라는 것을 알게 될 것입니다. 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:29 UTC 2013', machine='x86_64', processor='x86_64')
반환된 결과는 명명된 튜플이므로 아래 첨자를 기억할 필요 없이 다음과 같이 이름만으로 특정 속성을 지정할 수 있습니다.
>>> platform.uname().system 'Linux'
플랫폼 모듈에는 다음과 같이 위 속성에 대한 몇 가지 직접적인 인터페이스도 있습니다.
>>> platform.system() 'Linux' >>> platform.release() '3.7.4-204.fc18.x86_64'
linux_distribution() 함수는 현재 사용 중인 Linux 배포 버전에 대한 자세한 정보를 반환합니다. 예를 들어, Fedora 18 시스템에서 이 명령은 다음 정보를 반환합니다:
>>> platform.linux_distribution() ('Fedora', '18', 'Spherical Cow')
이 반환 결과에는 버전 릴리스 이름, 버전 및 코드 이름의 튜플이 포함됩니다. 특정 Python 버전에 대해 지원되는 배포판은 _supported_dists에 표시된 값을 통해 얻을 수 있습니다.
아아아아귀하의 Linux 배포판이 그 중 하나(또는 그 파생물 중 하나)에 속하지 않는 경우. 그러면 위 함수를 호출해도 유용한 정보가 표시되지 않을 가능성이 높습니다.
플랫폼 모듈의 마지막 기능인 Architecture() 기능을 살펴보겠습니다. 인수 없이 이 함수를 호출하면 다음과 같이 아키텍처 비트와 Python 실행 가능 형식이 포함된 튜플이 반환됩니다.
>>> platform._supported_dists ('SuSE', 'debian', 'fedora', 'redhat', 'centos', 'mandrake', 'mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo', 'UnitedLinux', 'turbolinux')
32비트 시스템에서는
>>> platform.architecture() ('64bit', 'ELF')
가 표시됩니다. 시스템에서 다른 실행 파일을 지정하면 다음과 같이 비슷한 결과를 얻게 됩니다:
>>> platform.architecture() ('32bit', 'ELF')
현재 실행 중인 Python 버전을 알아보려면 이 외에도 플랫폼 모듈의 다른 기능을 탐색하는 것이 좋습니다. 이 모듈이 이 정보를 얻는 방법을 알고 싶다면 PYthon 소스 디렉터리에 있는 Lib/platform.py 파일을 자세히 살펴보세요.
os 및 sys 모듈은 기본 엔디안과 같은 일부 시스템 속성을 얻을 수도 있습니다. 다음으로 Python 표준 라이브러리 모듈을 넘어 proc 및 sysfs 파일 시스템을 통해 Linux 시스템의 정보에 액세스할 수 있게 해주는 몇 가지 사항을 살펴보겠습니다. 파일 시스템을 통해 정보에 액세스하는 방법은 하드웨어 아키텍처에 따라 다릅니다. 따라서 이 기사를 읽거나 스크립트를 작성할 때 항상 이러한 파일에서 정보를 얻으려고 시도할 수 있다는 점을 기억하십시오.
/proc/cpuinfo 파일에는 시스템의 프로세서 장치에 대한 정보가 포함되어 있습니다. 예를 들어, Linux 명령 cat /proc/cpuinfo의 Python 버전이 수행하는 작업은 다음과 같습니다.
>>> platform.architecture(executable='/usr/bin/ls') ('64bit', 'ELF')
Python 2 또는 Python 3을 사용하여 이 프로그램을 실행하면 화면에 /proc/cpuinfo의 모든 내용이 표시됩니다. (위 프로그램에서는 rstrip() 메서드를 사용하여 각 줄 끝의 개행 문자를 제거합니다. )
아래 코드는 startwith() 문자열 메서드를 사용하여 프로세서 장치의 모드를 표시하는 방법을 보여줍니다.
아아아아이 프로그램을 실행하면 각 프로세서 장치의 모드 이름이 표시됩니다. 예를 들어, 내 컴퓨터에 표시되는 내용은 다음과 같습니다.
아아아아지금까지 우리가 사용하고 있는 시스템의 아키텍처를 알아내는 두 가지 방법을 살펴보았습니다. 기술적으로 정확합니다. 두 방법 모두 실제로 시스템이 실행 중인 커널 아키텍처를 보고하므로 컴퓨터가 64비트이지만 32비트 커널을 실행하는 경우 위의 방법은 여전히 32비트 아키텍처에 대해 표시됩니다. /proc/cpuinfo에 나열된 플래그에서 lm 플래그를 찾아 컴퓨터의 실제 아키텍처를 찾을 수 있습니다. lm 플래그는 긴 모드를 나타내며 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的内容成为标准的数据结构,譬如字典(dictionary)。这个注意很简单:如果你查看这个文件的内容,你就会发现对于每个处理器单元,都有好些键值对(在先前的例子中,我们打印了每个处理器的模型名,即模型名就是关键字)。不同的处理器 单元的信息可以使用空白行隔开。构造一个字典数据结构包含每个处理器单元的关键字是很简单的。对于每个关键字,对于处理器单元的值都在/proc/cpuinfo文件中。下面的代码将会指导你怎么做。
#!/usr/bin/env/ python """ /proc/cpuinfo as a Python dict """ from __future__ import print_function from collections import OrderedDict import 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__'块中演示的那样)。上面的程序每次执行后都会打印每个处理器单元的模型名(如通过cpuinfo[processor]['model name']语句表明的那样)
Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz
和/proc/cpuinfo相似,文件/proc/meminfo包含了你电脑的主存的信息。下面的这个程序创建了一个使用这个文件的内容填充的字典。
#!/usr/bin/env python from __future__ import print_function from 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 kB Free memory: 249508 kB
接下来,我们会探索我们电脑系统的网络设备。我们将会获得系统的网络接口,以及当系统重启之后通过它们数据发送和接受数据的信息。 /proc/net/dev文件让这些信息可用。如果你检查了这个文件的内容,你就会注意到头一两行包含了头信息等等,这个文件第一列是网络接口名,第二和第三列显示了接收和发送的字节数信息(例如总发送字节数,包数,错误等等)。这里我们所感兴趣的就是他哦难过不同的网络设备提取出总发送数据和接收数据。下面的代码展示了怎么从/proc/net/dev文件中提取出这些信息。
#!/usr/bin/env python from __future__ import print_function from 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))
当你运行上面的程序,下面这个输出就会显示从你最近重启之后网络设备总接收和发送的数据,单位为兆。
em1: 0.0 MiB 0.0 MiB wlan0: 2651.40951061 MiB 183.173976898 MiB
你可以使用持久的数据存储机制来连接,来写出自己的数据使用监控程序。
/proc目录包含了所有正运行的进程目录。这些目录的名字和进程的标识符是一样的。所以,如果你遍历/proc目录下那些使用数字作为它们的名字的目录,你就会获得所有现在正在运行的进程列表。在下面的代码中process_list()函数返回所有现在正在运行的进程的标识符列表。当你执行这个程序后,这个列表的长度就是在系统上运行的总进程数。
#!/usr/bin/env python """ List of all process IDs currently active """ from __future__ import print_function import os def 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)))
上面的程序当执行后会显示和下面类似的输出:
Total number of running processes:: 229
每个进程目录包含了一些其他文件和目录,如进程命令的调用,它正使用的共享库以及其它的。
下一个程序通过读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_function import glob import re import os # Add any other device pattern to read from dev_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 GiB Device:: /sys/block/mmcblk0, Size:: 3.70703125 GiB
当我运行该程序的时候,有个SD内存卡插在电脑上,因此你会看到程序检测到了它。你也可以扩展该程序识别其它块设备(比如虚拟硬盘)。
linux中命令行使用工具是无所不在的[@Lesus 注:曾有人说过:linux没有了命令行就是个渣。],它允许人么指定命令行参数来定制程序的默认行为。argparse模块就提供了和linux命令行实用工具类似的接口。下面的代码展示了程序如何获得系统上的所有用户以及打印它们的登录shell(使用了pwd标准库模块):
#!/usr/bin/env python """ Print all the users and their login shells """ from __future__ import print_function import pwd # Get the users from /etc/passwd def 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_function import pwd import argparse import 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/passwd def 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)
使用--help选项执行上面的程序,你会看到友好的帮助信息:可选项以及它们的作用。
$ ./getusers.py --help usage: getusers.py [-h] [--no-system] User/Password Utility optional arguments: -h, --help show this help message and exit --no-system Specify to omit system users
上面程序使用的一个例子,如下所示:
$ ./getusers.py --no-system gene:/bin/bash
当你传入一个非法的参数,这个程序就会发牢骚(报错)
$ ./getusers.py --param usage: getusers.py [-h] [--no-system] getusers.py: error: unrecognized arguments: --param
在上面的程序中,我们简单的理解了如何使用argparse模块。parser = argparse.ArgumentParser(description="User/Password Utility")语句创建了一个带说明程序是做什么的可选描述的ArgumentParser对象,
然后,我们添加参数。我们想要程序能够识别接下来这条语句 add_argument()。parser.add_argument('--no-system', action='store_true', dest='no_system', default = False, help='Specify to omit system users')。第一个方法的参数是当系统调用这个程序,程序使用着将要提供这个参数的名称,接下来的参数acton=store_true表明它是一个布尔选择。那就是说,它真或假影响程序的某些行为。dest为可定制化参数,它的值可以提供给程序使用。假如这个值用户不提供,这个值默认false。最后的参数程序显示的帮助信息。最后,参数被解析通过args=parser.parse_args()方法。一旦解析方法被做,用户选项的值能够被抓取到通过相应的语法参数option_dest,当你配置参数的时候,option_dest是一个你指定的目的变量。getusers(args.no_system)这条语句使用用户提供参数的值将会回调getusers()方法。
下面的程序展示了如何指定非布尔类型的选项。该程序是对第6个程序的重写,附加了一个选项用于指定你感兴趣的网络设备。
#!/usr/bin/env python from __future__ import print_function from collections import namedtuple import argparse def netdevs(iface=None): ''' 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 not iface: 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)) else: if line[0].strip() == iface: 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__': parser = argparse.ArgumentParser(description='Network Interface Usage Monitor') parser.add_argument('-i','--interface', dest='iface', help='Network interface') args = parser.parse_args() netdevs = netdevs(iface = args.iface) for dev in netdevs.keys(): print('{0}: {1} MiB {2} MiB'.format(dev, netdevs[dev].rx, netdevs[dev].tx))
当你不带任何参数执行程序的时候,程序的行为与之前的版本完全一致。然后,你也可以指定感兴趣的网络设备。例如:
$ ./net_devs_2.py em1: 0.0 MiB 0.0 MiB wlan0: 146.099492073 MiB 12.9737148285 MiB virbr1: 0.0 MiB 0.0 MiB virbr1-nic: 0.0 MiB 0.0 MiB $ ./net_devs_2.py --help usage: net_devs_2.py [-h] [-i IFACE] Network Interface Usage Monitor optional arguments: -h, --help show this help message and exit -i IFACE, --interface IFACE Network interface $ ./net_devs_2.py -i wlan0 wlan0: 146.100307465 MiB 12.9777050018 MiB
在本文的帮助下,你可能已经可以写一个或多个有用的脚本,就像其它linux命令一样,你想要每天都使用它们。最简单的方式是将脚本设置为可执行的,然后为脚本设置一个BASH别名。你也可以移除.py扩展名,然后将脚本放在诸如/usr/local/sbin这样的标准位置。
除了本文中已经提到的标准库模组,还有很多其它有用的标准模组:subprocess、ConfigParser、readline和curses。
在这个阶段,依靠你自己使用Python的经验,探索Linux内部,你可以参考下面的任一方式。如果你曾经需要写很多shell脚本/命令流水线来探索Linux内部,那么试一下Python。如果你想要一个更简单的方式来编写执行很多任务的实用程序脚本,那么试一下Python。最后,如果你已经使用Python在Linux上别写其它目的的程序,那么试一下用Python探索Linux内部。
Lists
Tuples
Namedtuples
OrderedDict
split()
strip() rstrip() and other string methods
Reading and writing files
os module
platform module
pwd module
spwd module
grp module
subprocess module
ConfigParser module
readline module
Long Mode
/proc file system
sysfs
原文地址:http://amitsaha.github.io/site/notes/articles/python_linux/article.html
위 내용은 Python을 사용하여 Linux의 시스템 정보를 얻는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!