A tutorial on using Python to make tools to detect Linux running information _python

Source: Internet
Author: User
Tags data structures extend glob python script

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

What kind of python?

When I mention Python, I usually refer to CPython 2 (2.7, to be exact). When the same code does not run at CPython3 (3.3), we explicitly point it out and give the alternative code to explain the differences between them. Make sure you've installed the CPython, enter Python or Python3 in the terminal, and you'll see the Python prompt appear in your terminal.

Note that all of the scripting programs use #!/usr/bin/env python as the first line, meaning we want the Python parser to run these scripts. Therefore, if you use the chmod +x your-script.py command to add executable permissions to your script, you can use the./your-script.py command to run your script directly (you'll see this in this article)

Explore the Platform Module

The Platform module in the standard library has a large number of functions that let us examine various system information. Let's open the Python interpreter (translator: Just enter python directly at 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 16:44:29 UTC 2013 ', ' x86_64 ')

If you know the uname command on Linux, you realize that this function is an interface to the uname command. In Python 2, this function returns a tuple of system type (or kernel type), host name, version number, distribution number, host hardware architecture, and processor type. You can use an index to get a single property, like this:

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

In Python 3, this function returns a default named tuple:

>>> platform.uname ()
 
uname_result (system= ' Linux ', node= ' Fedora.echorand ',
release= ' 3.7.4-204. Fc18.x86_64 ', version= ' #1 SMP Wed 16:44:29 UTC 2013 ', machine= ' x86_64 ', processor= ' x86_64 '
)

Because the return value is a default named tuple, we can easily get a single property through the variable name without remembering the subscript for each attribute, like this:

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

The Platfrom module also provides a number of direct interfaces to obtain the above attribute values, like these:

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

function Linx_distribution () returns the details of the Linux distribution you are using. For example, in a Fedora 18 system, this command returns the following message:

>>> platform.linux_distribution (
' Fedora ', ' spherical ', ' Cow ')

The return value is a tuple consisting of the release version name, version number, and symbol. You can use the _supported_dists property to print which versions of your Python version support:

>>> platform._supported_dists
(' SuSE ', ' Debian ', ' fedora ', ' redhat ', ' CentOS ', ' Mandrake ',
' Mandriva ', ' Rocks ', ' slackware ', ' yellowdog ', ' Gentoo ',
' unitedlinux ', ' turbolinux '

If your Linux distribution is not one of the above (or one of the derivative versions), then you will not see any useful information when you call the above function.

The last Platfrom function we're going to explore is the architecture () function. When you do not add any arguments to call this function, this function returns a tuple of the bit schema and the Python executable file format. Like what:

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

In a 32-bit Linux system, you'll see:

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

If you specify any other system executable as a 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 to let you find the version of Python that you are currently using. If you really want to know how this module gets this information, you can look at the lib/platfrom.py file in the Python source directory.

The OS and SYS modules are also a useful module for acquiring the unified properties, just like local byteorder. Next, we will not use Python your standard library module to explore some common ways to get information about Linux systems, this time through the proc and SYS file systems. It is to be noted that the information obtained through these file systems differs in different hardware architectures. Therefore, keep it in mind when reading this article and writing scripts to get system information from these files.

CPU Information

/proc/cpuinfo This file contains the processing unit information for your system. For example, here's a Python script with the same functionality as the input cat/proc/cpuinfo at 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 with Python 2 or Python 3, you'll see that everything in the/proc/cpuinfo file is displayed on your screen. (In the above script, the Rstrip () method removes the newline character of each line)

The next code list uses the Startwith () string method to display your computer's processing unit model.

#! /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 number of all the processing units for your machine. For example, here's what I see on my Computer:

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

So far we've had several ways to get our computer architecture. Technically speaking, all of these methods actually present the architecture of the system kernel that you are running. So if your computer is actually a 64-bit machine, but runs a 32-bit kernel, the above method will show you that your computer is a 32-bit architecture. To find the correct architecture for your computer, you can view the LM attribute of the list of properties in/proc/cpuinfo. The 1m property represents long mode and is only present on a 64-bit schema computer. The following script shows you how to do it:

#! /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
    # detail s 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 have seen, we are able to access/proc/cpuinfo files and use simple text processing techniques to read the information we are looking for. In order to provide data to other programs in a friendly way, the best approach might be to convert the content obtained from/proc/cpuinfo into a standard data mechanism, such as converting to a dictionary type. The method is simple: If you look at this file, you will find that each processing unit is 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.) Information for each of the different processor units is separated by a blank line. This allows us to build dictionary data structures conveniently with each processing unit datum as a key. These keys have a value (value) and each value corresponds to all the information in the/proc/cupinfo file for each processing unit. The next code list tells you what to do:

 #!/usr/bin/env/python ""/proc/cpuinfo as a python dict "" "from __future__ import pri Nt_function from Collections import ordereddict import Pprint def cpuinfo (): "Return" information In/proc/cpuin
  Fo 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=n Procs+1 # Reset Procinfo=ordereddict () Else:if len (Line.split (': ')) = = 2:procinf O[line.split (': ') [0].strip ()] = Line.split (': ') [1].strip () else:procinfo[line.split (': ') [0].strip ()] = ' Return cpuinfo if __name__== ' __main__ ': Cpuinfo = Cpuinfo () to processor in Cpuinfo.keys (): Print (Cpuinf o[processor][' model name ') 

This code uses a ordereddict (ordered dictionary) instead of a commonly used dictionary type, in order to save the key value pairs found in the file before they are sorted. So, first show the data information for the first processing unit and then the second, and so on. If you call this function, it will return a dictionary type to you. Every key in a dictionary is a processing unit. You can then use the key to filter the information you are looking for (as shown in the If __name= ' __main__ ' statement block). When the above script is run, the model name of each processing unit is printed again (shown by the print (cpuinfo[processor][' model name ') statement)

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

Memory information

Like/proc/cpuinfo, the/proc/meminfo file contains the main memory information for your computer. The next script produces a dictionary containing the contents of the file and prints it out.

#!/usr/bin/env python from
 
__future__ import print_function from
collections import Ordereddict
 
def Meminfo (): ' Return to 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 seen earlier, you can also use a specific key to get any information you want (in the If __name__== ' __main__ ' statement is displayed quickly). When you run this script, you can see output similar to the following:

Total memory:7897012 MB free
memory:249508 kb

Network statistics

Below, we will explore the network devices of our computer system. We will retrieve the network interface of the system and the byte data sent and received after the system is turned on. This information can be obtained in the/proc/net/dev file. If you have reviewed the contents of this file, you will find that the first two lines contain header information-i.e. In the file, the name of the network interface is displayed, and the second and third columns show the bytes received and transmitted (for example, total bytes sent, packet count, error statistics, etc.). What we are interested in is how to get the total data sent and received for different network devices. The next code listing shows how we can present this information from/proc/net/dev:

#!/usr/bin/env python
__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.read Lines ()
 
  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 your most recent reboot is output in MIB units. As shown in the following:

em1:0.0 MIB 0.0 MIB
wlan0:2651.40951061 MIB 183.173976898 MIB

You may be using 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 with the corresponding process ID. So if you walk through all the digitally named directories in the/proc directory, you get a list of all the current running processes. The Process_list () function in the following code list returns a list containing all the currently running process IDs. The length of this list is equal to the total number of running processes in the system, as you can see from running this script:

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

When you run the above script, the output is similar to the following output:

Each process directory contains a large number of other files and directories that contain a variety of shared libraries and other information about the process invocation commands.

Block device

The next script lists all the block device information by accessing the SYSFS virtual file system. You can find all the block devices on the system in the/sys/block directory. Therefore, you will have/SYS/BLOCK/SDA,/SYS/BLOCK/SDB and other similar directories on your system. To find these devices, we can traverse the/sys/block directory and then use a simple regular expression to match what we're looking for.

#!/usr/bin/env python "" "Read blocks device data from Sysfs" "' from
 
__future__ Import print_function< C5/>import glob
Import re
import os
 
# ADD any other device pattern to read
from dev_pattern = [' sd.* ', ' MMC Blk* ']
 
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 output similar to the following:

Device::/SYS/BLOCK/SDA, Size:: 465.761741638 GiB
Device::/sys/block/mmcblk0, Size:: 3.70703125 GiB

When I run this script, I insert an extra SD card. So you'll see that the script detects it (the second line of output above, the translator). You can also extend the script to identify other block devices (such as virtual hard disks).

Building Command line tools

Allowing users to specify command-line arguments to customize the default behavior of a program is a common feature of all Linux command-line tools. The Argparse module enables you to have an interface similar to the built-in tool interface. The next code listing shows a program that gets all the users on your system and prints the corresponding landing shells.

#!/usr/bin/env python "" "Print all of the
users and their login shells" "" from
 
__future__ import Print_fun ction
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 ()

When you run the above script, it prints out all the users on your system and their login shell

Now, let's say you want the script user to be able to choose whether they want to see other users of the system (such as Daemon,apache). We do this by using the Argparse module to extend the code, just like the following code.

#!/usr/bin/env python "" "" "Utility to play around with the users and passwords on a Linux system" "from __future__ Impor T 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.STARTSW ITH (' Uid_max '): Uid_max = Int (Line.split () [1].strip ()) return to Uid_min, Uid_max # get the users From/etc/pas SWD def getusers (no_system=false): uid_min, Uid_max = Read_login_defs () if uid_min is none:uid_min = 1000 I F Uid_max is None:uid_max = 60000 users = Pwd.getpwall () for user into 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 (AR

 Gs.no_system)

Using the –HELP option to run the above script, you will see a friendly help with options (and functions)

$./getusers.py--help
usage:getusers.py [-h] [--no-system] user/password Utility Optional arguments
 
:
 -H,--help show this Help and  exit
 --no-system Specify to omit system users

An example of the above script is called as follows:

$./getusers.py--no-system
Gene:/bin/bash

When you pass an invalid argument, the script complains:

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

Let's take a quick look at how we use the Argparse module Parser=argparse 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 have the next line of code: parser.add_argument (' –no-system ', action= ' store_true ', dest= ' no_system ', default = False, help= ' Specify To omit system users, use the Add_argument () method to add some parameters to allow the script to recognize command-line options. The first parameter of this method is the option name provided by the script consumer as a parameter when invoking the script. The next parameter, Action=store_true, indicates that this is a Boolean option. That is to say, there is a certain degree of this parameter will have an impact on the program. The dest parameter specifies a variable to hold the option value and provide it to the script for use. If the user does not provide an option, the default value of Default=false can be set to false. The last parameter is a script that shows help on this option. Finally, use the Parse_args () method to process the parameters: Args=parser.parse_args (). Once processed, use Args.option_dest to get the user-supplied option value, where option_dest is the dest variable you specify when you set the parameter. This line of code getusers (Args.no_system) calls Getusers () using the user-supplied No_system option value as a parameter.

The next script shows you how you can provide the user with a non boolean option in your script. This script rewrites code listing 6, adding additional options that allow you to specify which network devices you are interested in.

#!/usr/bin/env python __future__ import print_function from collections import namedtuple import Argparse def netd EVS (Iface=none): "RX and TX bytes for each of the network devices" with open ('/proc/net/dev ') as F:net_du MP = 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].s Plit () [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 = Argpa Rse.
            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 (): PR

 Int (' {0}: {1} MIB {2} MIB '. Format (Dev, Netdevs[dev].rx, netdevs[dev].tx))

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

$./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 Us Age Monitor
 
Optional arguments:-H,--help show this help message      and exit-I
 iface,--interface iface< C12/>network interface
 
$./net_devs_2.py-i wlan0
wlan0:146.100307465 MIB 12.9777050018 MIB

To make your script run anywhere.

With the help of this article, you may have been able to write yourself one or more useful scripts that you want to use every day, just like other Linux commands. The easiest way to do this is to make the scripts run (add run permissions to the script) and set the bash alias (bash alias) to 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 so far in this article, there are many other standard libraries that might be useful: Subprocess,configparser, readline and curses.

Next?

At this stage, depending on your own Python experience and exploring Linux in depth, you choose one of the following ways. If you've written a lot of shell scripts or command channels to delve deeper into various Linux, try Python. If you want to write your own scripting tools to perform various tasks in a simpler way, try Python. Finally, if you've already used other kinds of Python to program on Linux, I hope you'll be happy to explore Linux in depth with 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.