Use Python to create a tool for detecting Linux running information.

Source: Internet
Author: User
Tags glob

Use Python to create a tool for detecting Linux running information.

In this article, we will explore how to use the Python language as a tool to detect various running information of Linux systems. Let's learn it together.

What type of Python?

When I mention Python, I generally refer to CPython 2 (accurate to 2.7 ). When the same Code cannot be run in CPython3 (3.3), we explicitly point it out and provide alternative code to explain the differences between them. Make sure that you have installed CPython. Input python or python3 on the terminal and you will see the Python prompt on your terminal.

Please note that all script programs will start #! /Usr/bin/env python is the first line, which means we need the Python parser to run these scripts. So if you use the chmod + x your-script.py command to add executable permissions to your script, you can use. /your-script.py commands run your script directly (you will see this operation in this article)

Explore the platform Module

The platform Module in the standard library has a large number of functions for us to check various system information. Let's open the Python interpreter (TRANSLATOR: Simply enter python in the command line) and explore some of the functions. Let's start with the platform. uname () function:
 

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

If you know the uname command on Linux, you will realize that this function is an interface of the uname command. In Python 2, this function returns a tuple consisting of the system type (or kernel type), host name, version number, Release number, host hardware architecture, and processor type. You can use indexes to obtain a single attribute, as shown in the following code:
 

>>> platform.uname()[0]'Linux'

In Python 3, this function returns a default-named tuples:
 

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

Because the returned value is a default name tuples, we can easily get a single attribute through the variable name without remembering the subscript of each attribute, as shown in the following code:
 

>>> platform.uname().system'Linux'

The platfrom module also provides some direct interfaces to obtain the preceding attribute values, such:
 

>>> platform.system()'Linux' >>> platform.release()'3.7.4-204.fc18.x86_64'

The linx_distribution () function returns the details of the Linux release you are using. For example, in the Fedora 18 system, this command returns the following information:
 

>>> platform.linux_distribution()('Fedora', '18', 'Spherical Cow')

The returned value is a tuple consisting of the release version name, version number, and code. You can print the versions supported by your Python version through the _ supported_dists attribute:
 

>>> platform._supported_dists('SuSE', 'debian', 'fedora', 'redhat', 'centos', 'mandrake','mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo','UnitedLinux', 'turbolinux')

If your Linux release version is not one of the above (or one of the derivative versions), you will not see any useful information when calling the above functions.

The last platfrom function we want to explore is the architecture () function. When you do not add any parameters to call this function, this function returns a tuple consisting of a bit architecture and a Python executable file format. For example:
 

>>> platform.architecture()('64bit', 'ELF')

In a 32-bit Linux system, you will see:
 

>>> platform.architecture()('32bit', 'ELF')

If you specify any other system executable program as the parameter, you will get a similar result:
 

>>> platform.architecture(executable='/usr/bin/ls')('64bit', 'ELF')

We encourage you to explore other functions in the platfrom module so that you can find your current Python version. If you really want to know how this module gets this information, you can go to the Lib/platfrom. py file in the Python source code directory.

The OS and sys modules are also useful modules for retrieving system attributes, just like local BYTEORDER. Next, we will not use the Python standard library module to explore some common methods for obtaining Linux system information. This time we will implement it through proc and sys file systems. Note that the information obtained through these file systems varies in different hardware architectures. Therefore, remember to read this article and write scripts to obtain system information from these files.

CPU Information

The/proc/cpuinfo file contains the processing unit information of your system. For example, there is a Python script with the same function as input cat/proc/cpuinfo on the command line.
 

#! /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'))

When you run this script using Python 2 or Python 3, you will see all the content of the/proc/cpuinfo file displayed on your screen. (In the above script, the rstrip () method removes the line breaks for each row)

The next code list uses the startwith () string method to display the processing unit model of your computer.
 

#! /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)

When you run this script, you will see the model of all the processing units on your machine. For example, here is what I see on my computer:
 

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

So far, we have several methods to obtain the architecture of our computer system. Technically, all these methods actually present the architecture of the system kernel that you run. Therefore, if your computer is actually a 64-bit machine but runs a 32-bit kernel, the above method will show that your computer is in a 32-bit architecture. To find out the correct architecture of the computer, you can view the lm attributes in the/proc/cpuinfo attribute list. The 1 m attribute represents the Long mode and only appears on a 64-bit computer. The following script shows you how to do this:

#! /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')

As we can see, we can access the/proc/cpuinfo file and use simple text processing technology to read the information we are looking. To provide data to other programs in a friendly manner, the best way is to convert the content obtained from/proc/cpuinfo to a standard data organization, for example, to a dictionary type. The method is simple: if you read this file, you will find that each processing unit exists in the form of a key-Value Pair (in the previous example, when we print the processor model name, the model name here is a key .) The information of each processor unit is separated by blank lines. This allows us to easily construct a dictionary data structure with each processing unit metadata as the key. These keys have a value, and each value corresponds to all the information of each processing unit in the/proc/cupinfo file. The next code list shows you how to do this:

#!/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'])

This Code uses an OrderedDict (ordered dictionary) to replace common dictionary types. The purpose is to sort the key-value pairs found in the file before saving them. Therefore, the data information of the first processing unit is displayed first, followed by the second, and so on. If you call this function, it returns a dictionary type to you. Each key in the dictionary is a processing unit. Then you can use the key to filter the information to be found (as shown in the if _ name = '_ main _' statement block ). When the preceding script runs, the model name of each processing unit is printed again (displayed through the print (cpuinfo [processor] ['model name'] Statement)
 

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

Memory Information

Similar to/proc/cpuinfo, the/proc/meminfo file contains the master storage information of your computer. The next script generates a dictionary containing the file content and outputs it.

#!/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']))

As shown above, you can also use a specific key to obtain any information you want (displayed in the if _ name __= = '_ main _' statement ). When you run this script, you can see the output similar to the following:
 

Total memory: 7897012 kBFree memory: 249508 kB

Network statistics

Next, we will explore the network devices of our computer system. We will retrieve the system's network interfaces and bytes of data sent and received after the system is enabled. This information can be obtained in the/proc/net/dev file. If you have reviewed the content of this file, you will find that the first two rows contain header information-I. e. the first column of the file is the network interface name, and the second and third columns show the received and transmitted bytes (such as the total sent bytes, number of packets, error statistics, and so on ). We are interested in how to obtain the total amount of data sent and received by different network devices. The next code list shows how we put forward this information from/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))

When you run the above script, the data received and sent from your network device after the last restart is output in MiB. As shown below:
 

em1: 0.0 MiB 0.0 MiBwlan0: 2651.40951061 MiB 183.173976898 MiB

You may use a persistent storage mechanism and this script to write your own data using a monitoring program.

Process

The/proc directory also contains the directory of each running process. The names of these directories are named by the corresponding process ID. Therefore, if you traverse all the directories named by numbers under the/proc directory, you will get a list of IDs of all currently running processes. The process_list () function in the code list below returns a list containing the IDs of all currently running processes. The length of this list is equal to the total number of processes running on the system, as you can see when running this script:
 

#!/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)))

When running the above script, the output result is similar to the following output:

Each process directory contains a large number of other files and directories, which contain various information about process invocation commands, shared libraries, and others.

Block Device

The following script lists all block device information by accessing the sysfs Virtual File System. You can find all block devices on the system under the/sys/block directory. Therefore, your system will have/sys/block/sda,/sys/block/sdb and other similar directories. To find these devices, we can traverse the/sys/block directory and then use a simple regular expression to match the content we are looking.

#!/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()

If you run this script, you will see the output result similar to the following:
 

Device:: /sys/block/sda, Size:: 465.761741638 GiBDevice:: /sys/block/mmcblk0, Size:: 3.70703125 GiB

When I run this script, I insert an additional SD card. So you will see this script detect it (the second line output above, the Translator's note ). You can also expand this script to identify other Block devices (such as virtual hard disks ).

Build a command line tool

The default behavior that allows users to specify command line parameters to customize programs is a common feature of all Linux Command Line tools. The argparse module allows your program to have a interface similar to the built-in tool interface. The next code list shows a program that gets all the users on your system and prints the corresponding logon 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()

When running the above script, it will print all the users on your system and their Login Shells

Now, let's assume that you want the script user to choose whether to view other users (such as daemon and apache) in the system ). We use the argparse module to extend the previous code to implement this function, just like the following code.

#!/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)

Use the-help option to run the above script. You will see a friendly help information with options (and functions ).
 

$ ./getusers.py --helpusage: 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

An example of the above script is called as follows:
 

$ ./getusers.py --no-systemgene:/bin/bash

When you pass an invalid parameter, the script reports an error:
 

$ ./getusers.py --paramusage: getusers.py [-h] [--no-system]getusers.py: error: unrecognized arguments: --param

Let's take a simple look at how we used the parser = argparse of the argparse module in the above script. argumentParser (description = 'user/Password Utility ') This line of code creates a new ArgumentParser object using an optional parameter that describes the role of the script.

Then, we will run the following code: parser. add_argument ('-no-system', action = 'store _ true', dest = 'no _ system', default = False, help = 'specify to omit system users ') the add_argument () method is used to add some parameters so that the script can recognize the command line options. The first parameter of this method is the option name provided by the script user as the parameter when calling the script. The next parameter action = store_true indicates that this is a Boolean option. That is to say, whether or not this parameter affects the program to a certain extent. The dest parameter specifies a variable to save the option value and provide it to the script. If you do not provide the option, you can set the default value to False by default = False. The last parameter is the script that displays help information about this option. Finally, the parse_args () method is used to process the parameter: args = parser. parse_args (). Once the processing is complete, use args. option_dest to obtain the option value provided by the user. Here, option_dest is the dest variable specified when you set the parameter. This line of code getusers (args. no_system) uses the user-provided no_system option value as the parameter to call getusers ().

The following script shows how to provide a non-Boolean option in your script. This script overwrites code listing 6 and adds additional options to allow you to specify network devices that you are interested in.

#!/usr/bin/env pythonfrom __future__ import print_functionfrom collections import namedtupleimport 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))

When you do not provide any parameters to run the script, the running result is actually the same as that of the previous version. However, you can also specify network devices that you may be interested in. For example:
 

$ ./net_devs_2.py em1: 0.0 MiB 0.0 MiBwlan0: 146.099492073 MiB 12.9737148285 MiBvirbr1: 0.0 MiB 0.0 MiBvirbr1-nic: 0.0 MiB 0.0 MiB $ ./net_devs_2.py --helpusage: 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 wlan0wlan0: 146.100307465 MiB 12.9777050018 MiB

Enable your script to run anywhere

With the help of this article, you may already be able to write one or more useful scripts for yourself, which you want to use every day, just like other Linux commands. The simplest way to achieve this is to make these scripts run (add the running permission to the script, the Translator's note) and set the BASH alias (BASH alias) for these commands ). You can also delete the. py suffix and put the file in a standard location, such as/usr/local/sbin.

Other useful standard library modules

In addition to the standard libraries we have seen in this article so far, there are many other standard libraries that may be useful: subprocess, ConfigParser, readline, and curses.

Next?

At this stage, you can choose one of the following methods based on your own Python experience and in-depth exploration of Linux. If you have written a large number of shell scripts or command pipelines to explore a variety of Linux systems, please try Python. If you want to write your own script tool to execute various tasks in a simpler way, try Python. Finally, if you have used other types of Python programming on Linux, I wish you a pleasant exploration of Linux using Python.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.