Use Python to write a tutorial for the command line tools for the UNIX system class

  • 2020-05-10 18:22:18
  • OfStack

The introduction

Can you write a command-line tool? Maybe you can, but can you write a really useful command-line tool? This article discusses using Python to create a robust command-line tool with built-in help menus, error handling, and option handling. For some strange reasons, many people don't know about it. The standard library has all the tools you need to make an extremely powerful *NIX command-line tool.

It can be argued that Python is the best language for making *NIX command-line tools, because it works in the "batteries-included" philosophy and emphasizes providing readable code. But just as a reminder, when you discover how easy it is to create a command-line tool using Python, these ideas are dangerous, and your life could be turned upside down. As far as I know, no article has been published detailing the creation of a command-line tool using Python, so I hope you enjoyed this article.
Set up the

The optparse module in the Python standard library does most of the trivial work of creating a command-line tool. optparse is included in Python 2.3, so the module will be included in many *NIX operating systems. If, for some reason, you're using an operating system that doesn't include the required modules, it's good to know that the latest version of Python has been tested and compiled into almost any *NIX operating system. The systems supported by Python include IBM? AIX & # 63; , HP-UX, Solaris, Free BSD, Red Hat Linux? , Ubuntu, OS X, IRIX, and even several Nokia phones.
Create the Hello World command-line tool

The first step in writing a good command-line tool is to define the problem to solve. This is critical to the success of your tool. This is also important for solving problems in the simplest possible way. The KISS (Keep It Simple Stupid, keep it simple) guideline is explicitly adopted here. Add options and other functionality only after you have implemented and tested the planned functionality.

Let's start by creating the Hello World command-line tool. As suggested above, we define the problem in terms as simple as possible.

Problem definition: I want to create a command-line tool that prints Hello World by default and provides the option to print names of people who cannot be used.

Based on the above instructions, you can provide a solution that contains a small amount of code.
Hello World command line interface (CLI)

               


 #!/usr/bin/env python
     import optparse
     
     def main():
      p = optparse.OptionParser()
      p.add_option('--person', '-p', default="world")
      options, arguments = p.parse_args()
      print 'Hello %s' % options.person
     
     if __name__ == '__main__':
      main()

If you run this code, the expected output is as follows:


Hello world

But we can do much more with a little code. We can get the help menu automatically generated:


python hello_cli.py --help  
     Usage: hello_cli.py [options]
     
     Options:
     -h, --help      show this help message and exit
     -p PERSON, --person=PERSON

As you can see from the help menu, there are two ways to change the output of Hello World:


python hello_cli.py -p guido
Hello guido

We also implemented auto-generated error handling:


python hello_cli.py --name matz
Usage: hello_cli.py [options]
     
hello_cli.py: error: no such option: --name

If you haven't already used Python's optparse module, you might have been surprised and thought about all the incredible tools you can write with Python. If you're new to Python, you might be surprised at how easy Python makes 1 shear. The "XKCD" website has published a very interesting cartoon on the "Python is so simple" theme, which is included in the resources section.
Create useful command-line tools

Now that we've laid the groundwork, we can move on to creating tools to solve specific problems. For this example, we will use Python's network library and interactive tool called Scapy. Scapy works on most *NIX systems, can send packets at layers 2 and 3, and allows you to create very complex tools with only a few lines of Python code. If you want to start from scratch, make sure you have the necessary software installed correctly.

Let's define the new problem to solve first.

Question: I want to create a command-line tool that takes an IP address or subnet as a parameter and return either an MAC address or an MAC address list and their respective IP addresses to the standard output.

Now that we have clearly defined the problem, let me try to break it down into its simplest possible parts, and then solve the parts one by one. For this 1 problem, I see two separate parts. Part 1 is about writing a function that accepts an IP address or subnet range and returns an MAC address or an MAC address list. We can consider integrating this into a command-line tool after we have solved this problem.
Solution part 1: create the Python function that determines the MAC address via the IP address


arping

from scapy import srp,Ether,ARP,conf

conf.verb=0
ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="10.0.1.1"),
timeout=2)

for snd, rcv in ans:
print rcv.sprintf(r"%Ether.src% %ARP.psrc%")

The output of this command is:


    sudo python arping.py
    00:00:00:00:00:01 10.0.1.1

Note that using scapy to perform operations requires enhanced permissions, so we must use sudo. For the purposes of this article, I also changed the actual output to include the pseudo-MAC address. We have confirmed that we can find the MAC address at the IP address. We need to clean up this code to accept the IP address or subnet and return the MAC address and IP address pairs.
arping function


    #!/usr/bin/env python
    
    from scapy import srp,Ether,ARP,conf
    
    def arping(iprange="10.0.1.0/24"):
     conf.verb=0
     ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=iprange),
               timeout=2)
    
     collection = []
     for snd, rcv in ans:
      result = rcv.sprintf(r"%ARP.psrc% %Ether.src%").split()
      collection.append(result)
     return collection
    
    #Print results
    values = arping()
    
    for ip,mac in values:
    print ip,mac

As you can see, we wrote a function that takes an IP address or network and returns a nested IP/MAC address list. We are now ready for part 2, creating a command line interface for our tool.
Part 2 of the solution: create a command-line tool from our arping function

In this example, we combine the ideas from earlier in this article to create a complete command-line tool that solves our initial problem.
arping CLI

   


 #!/usr/bin/env python
  
  import optparse
  from scapy import srp,Ether,ARP,conf
  
  def arping(iprange="10.0.1.0/24"):
   """Arping function takes IP Address or Network, returns nested mac/ip list"""
  
   conf.verb=0
   ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=iprange),
        timeout=2)
  
   collection = []
   for snd, rcv in ans:
     result = rcv.sprintf(r"%ARP.psrc% %Ether.src%").split()
     collection.append(result)
   return collection
  
  def main():
   """Runs program and handles command line options"""
  
   p = optparse.OptionParser(description=' Finds MAC Address of IP address(es)',
                  prog='pyarping',
                  version='pyarping 0.1',
                  usage='%prog [10.0.1.1 or 10.0.1.0/24]')
  
  options, arguments = p.parse_args()
  if len(arguments) == 1:
   values = arping(iprange=arguments)
   for ip, mac in values:
    print ip, mac
  else:
   p.print_help()
  
  if __name__ == '__main__':
   main()

A few comments on the above script will help us understand how optparse works.

First, you must create an instance of optparse.OptionParser () and accept the following optional parameters:

 

description, prog, version, and usage

The meaning of these parameters is largely self-explanatory, but one thing I want to make sure of is that you should understand that optparse, while powerful, is not omnipotent. It has a well-defined interface and can be used to quickly create command-line tools.

Second, in the following line:

options, arguments = p.parse_args()

The purpose of this line is to divide the options and parameters into bits. In the code above, we expected exactly one parameter, so I specified that we must have only one parameter value and pass that value to the arping function.

       


   if len(arguments) == 1:
      values = arping(iprange=arguments)

To further illustrate, let's run the following command to see how it works:


Hello world

0

In the example above, the parameter is 10.0.1.1, and since there is only one parameter, as I specified in the conditional statement, it is passed to the arping function. If options exist, they are passed to options in the options, arguments = p.parse_args () method. Let's take a look at 1, what happens when we decompose the expected use case of the command-line tool and assign two parameters to that use case:


Hello world

1

According to the structure of the conditional statement I built for the parameters, if the number of parameters is not 1, it will automatically open the help menu:


Hello world

2

This is an important way to control how the tool works, because you can use the number of arguments or the name of a particular option as a mechanism for controlling the flow of the command-line tool. Since we covered the creation of options in the original Hello World example, let's add a few options to our command-line tool by slightly changing the main function:
arping CLI main function


def main():
 """Runs program and handles command line options"""

 p = optparse.OptionParser(description='Finds MAC Address of IP address(es)',
              prog='pyarping',
              version='pyarping 0.1',
              usage='%prog [10.0.1.1 or 10.0.1.0/24]')
 p.add_option('-m', '--mac', action ='store_true', help='returns only mac address')
 p.add_option('-v', '--verbose', action ='store_true', help='returns verbose output')

 options, arguments = p.parse_args()
 if len(arguments) == 1:
  values = arping(iprange=arguments)
  if options.mac:
   for ip, mac in values:
    print mac
  elif options.verbose:
   for ip, mac in values:
    print "IP: %s MAC: %s " % (ip, mac)
  else:
   for ip, mac in values:
    print ip, mac

 else:
  p.print_help()

The main change is to create conditional statements based on whether or not an option is specified. Note that, unlike the Hello World command-line tool, we only use the option as our tool's true/false signal. In the case of the WSD option, if this option is specified, our conditional statement elif will print only the MAC address.

Here is the output of the new option:
arping output


Hello world

4

Learn more about creating command-line tools

Here are a few new ideas for further study. These ideas are explored in depth in the book I'm co-writing on Python *NIX systems management, to be published in mid-2008.
Use the subprocess module in the command line tool

The subprocess module, included in Python 2.4 or later, is the unified 1 interface for handling system calls and processes. You can easily replace the arping function above to use the arping tool for your particular *NIX operating system. Here's a rough example of the idea:
Subprocesses arping


Hello world

5

The following is the output of the function separate runtime: [root @ localhost] ~ # python pyarp. py [10] 00:16: CB: C3: B4:

Note that you use subprocess to get the output of the arping command, and you use the compiled regular expression to match the MAC address. Note that if you are using Python 2.3, you can replace subprocess with the popen module, which is available in Python 2.4 or later.
Use object relational mapper in command line tools such as SQLAlchemy or Storm with SQLite

Another possible option for the command-line tool is to use ORM (object relational mapper) to store data records generated by the command-line tool. There are quite a few ORM available for Python, but SQLAlchemy and Storm happen to be the two most commonly used. I decided to use Storm as an example by flipping a coin:
Storm ORM arping


Hello world

6

The main thing to focus on in this example is creating a class called NetworkRecord, which maps to the SQLite database "in memory." In the main function, I changed the output of the arping function to map to our record objects, update them to the database, and then fetch them back to print. This is obviously not a production-ready tool, but it serves as an illustrative example of the steps involved in using ORM in our tool.
Integrate config files in CLI
Python INI config syntax


Hello world

7

Next, we need to use the ConfigParser module to parse the above:
ConfigParser function


     #!/usr/bin/env python
     import ConfigParser
     
     def readConfig(file="config.ini"):
      Config = ConfigParser.ConfigParser()
      Config.read(file)
      sections = Config.sections()
      for machine in sections:
       #uncomment line below to see how this config file is parsed
       #print Config.items(machine)
       macAddr = Config.items(machine)[0][1]
       print machine, macAddr
     readConfig()

The output of this function is as follows:


Hello world

9

I leave the rest of the problem as an exercise for the reader to work out. What I'm going to do next is integrate this config file into my script so that I can compare the machine inventory recorded in my config file with the actual inventory of MAC addresses that appear in the ARP cache. The IP address, or hostname, is only useful when traced to a computer, but the tool we implemented could be useful for tracking the hardware address of a computer that exists on the network and determining whether it has previously appeared on the network.

conclusion

We first created a very simple but powerful Hello World command-line tool by writing a few lines of code. Then a complex networking tool was created using the Python network library. Finally, let's move on to some more advanced areas of research. In the advanced research section, we discussed the integration of the subprocess module, the object-relational mapper, and finally the configuration file.

Although not widely known, any reader with an IT background can easily create a command-line tool using Python. I hope this article has inspired you to create new command-line tools yourself.


Related articles: