DEV Community

PyBash
PyBash

Posted on • Originally published at pybash.hashnode.dev on

How to create a command-line utility using Python

Today I'll show you how to make a command-line utility using Python. So maybe you are wondering what is a command-line utility? Right? So, let's get started!

What is a command-line utility?


A command-line utility is a app that runs on the command-line. It does not have a GUI. It uses the command line interface or CLI to run and execute itself. If you want to read more about this, go here on wikipedia.

Prerequisites


I am going to be using Windows but the code will remain same for macOS and Linux as well. But the process for making a command-line utility are different. So, I guess I'll make another post soon, for macOS and Linux.

I am going to be using Visual Studio Code. You can use any code editor or text editor or IDE of your choice. Like, Sublime Text, Atom, Vim, etc.

I will be using Command Prompt(CMD) as my Terminal, you can use Windows Powershell as well.

Terms that will be used


  • CMD - Command Prompt
  • CLI - Command Line Interface
  • CLU - Command Line Utility
  • VSCode - Visual Studio Code
  • py - python3
  • conda - anaconda3
  • editor, code editor, text editor, ide - Your editor

Modules


All modules used in this project are from the stdlib. No modules need to be installed seperately. Modules used are:

  • os
  • sys

Project 1


What are we creating?

We are going to be making two utilities rather than one. The first one is going to be a file search utility that will search for a given keyword in file names in a specific directory, say, D: Drive. Then the utility lists all the files with the given word in their filename. And finally print the total no. of files that matched say, 23. And the fun part is we are not going to be taking these keyword and directory as input. Instead they should be passed as arguments after the utility name. And we are also going to take a look at Error and Exception Handling. For e.g. - Input:

C:\Users\username> search.py .py D:\Programming

Enter fullscreen mode Exit fullscreen mode

and output:

   D:\Programming\CLI-Utilities\search.py

   D:\Programming\CLI-Utilities\calc.py

   D:\Programming\etc.py

   No. of files matched = 3

Enter fullscreen mode Exit fullscreen mode

Input:

C:\Users\username> search.py python D:\Programming

Enter fullscreen mode Exit fullscreen mode

and output:

   No matching files in the given directory! Try another directory!

Enter fullscreen mode Exit fullscreen mode

Did you see that even though our file is in D:\Programming, we were access it from C:\Users\username?


Steps:

  1. Create a new folder named cli-utilities
  2. cd into that directory.
  3. Open it in your code editor. Type code . to open that directory in VSCode(if you are using VSCode).
  4. Make a new file named search.py.
  5. Now open search.py.

Actual Code

Ok, so let's get into the fun stuff now.

First we are going to be importing the required modules.

# search.py - CLI-Utilities - play4Tutorials.hashnode.dev, github.com/play4Tutorials
import os # stdlib
import sys # stdlib

Enter fullscreen mode Exit fullscreen mode

Next we are gonna check if the modules were imported successfully. If not we are gonna handle the error or exception and print out some useful details. We are going to be using try/except for that.

try:
    import os # stdlib
    import sys # stdlib
# Error and Exception Handling
except ImportError as e:
    print("Some modules could not be imported from stdlib('sys' and 'os')")

Enter fullscreen mode Exit fullscreen mode

Next, we are going to be getting the arguments passed along with the filename. We are going to be using sys.argv for that. We are going to be storing them in variables. sys.argv returns a list of all the arguments passed. Among them the first one is the file name itself. So we are going to be starting from sys.argv[1].

keyword = sys.argv[1] # Get the keyword that is to be searched for

Enter fullscreen mode Exit fullscreen mode

Now, we have the keyword stored in the variable keyword. So, let's get the path as well. But some times paths contain spaces, right? So, how do we deal with them? Since sys.argv splits arguments on spaces. Let's see.

path = " ".join(sys.argv[2:]) # Get the path where to search for the keyword
# Alright, so what we just did here is we used py's in-built `join()` function and joined
# all the strings from index 2 to n with a space. So that now if the path contained
# spaces, it will be joined with them.
fileMatched = 0 # Initialize filesMatched as 0. I'll explain this later.

Enter fullscreen mode Exit fullscreen mode

Finally, we will now start creating the main function named search_files(keyword, path)

def search_files(keyword, path):
    filesFound = filesMatched
    for root, dirs, files in os.walk(path): # Use os.walk(path) to loop through the given directory and get the root, dirs, and files
        for file in files: # Loop through all the files in the given directory.
            if keyword in file: # Check if keyword is in filename
                filesFound += 1 # Counter to see how many files matched
                print(" "+root+'\\'+str(file)+"\n") # Print out the full file path for every matching file
                if filesFound == 0: # Check if no files matched the given keyword and print the same
                    print("No matching files in the given directory! Try another directory!")
    # If 1 or more files are matched then print the same
    print(f"No. of files matched: {filesFound}")

Enter fullscreen mode Exit fullscreen mode

The above code kind of explains itself, however I'll explain it. First, we define a function named search_files(keyword, path). Next, we create a new variable named filesFound which is equal to filesMatched. we can't use filesMatched directly because then py would give an that it is an Unbound Variable. Ok, next up we create a for loop using os.walk(path)

for root, dirs, files in os.walk(path):

Enter fullscreen mode Exit fullscreen mode

So, what is os.walk() in Python? os.walk() is an in-built python method from the os module we imported at the beginning, remember? And it gives you back a list with all the names of the files in the given directory. And root, dirs, files is passed because it will return the root or the directory passed, then all the folders in the directory and then all the files in the directory. So we pass it store the values in the specific variables.

Then, we have another loop in the files list or array

for file in files:
    if keyword in file:
                filesFound += 1
                print(" " + root + '\\' + str(file) + "\n")
                if filesFound == 0:
                    print("No matching files in the given directory! Try another directory!")

Enter fullscreen mode Exit fullscreen mode

This simply loops through all the files. And checks if the given keyword is present in the filename. If it is present, then filesFound is incremented by 1 and the whole path to the file is printed. The " ", "\n" are just make it look abit better in the CLI. And the "\\"(double back-slash) is used because single back-slash is used to escape the quotes but double back-slash escapes the slash. Finally, another if condition checks whether the no. of matched files is 0 or not? If iit is zero, we print the same. Else, we go out of the if statement, then out the if statement, then out of the loop, and again out of the loop.

print(f"No. of files matched: {filesFound}")

Enter fullscreen mode Exit fullscreen mode

Over here, I am using f string to print out how many files matched the search. Using the filesFound variable. You can also use .format()

After that, we have another block of code for error and exception handling, which doesn't require any explanation.

try:
    search_files(keyword, path) # Calling the function
# Error and Exception Handling
except FileNotFoundError as e:
    print("Error: FileNotFoundError occured! Make sure you entered the correct path and entered it correctly.")
except Exception as e:
    print(f"Error: Some Error occured! \nDetails: {e}")

Enter fullscreen mode Exit fullscreen mode

Again, I am using f strings. You can use .format() as well.


And finally, the finished code looks like this.

# search.py - CLI-Utilities - play4Tutorials.hashnode.dev, github.com/play4Tutorials
try:
    import os # stdlib
    import sys # stdlib
# Exception and Error Handling
except ImportError as e:
    print("Error: Some modules could not be imported from stdlib('sys' and 'os')")

keyword = sys.argv[1] # Get the keyword that is to be searched for
path = " ".join(sys.argv[2:]) # Get the path where to search for the keyword

filesMatched = 0 # Initialize filesMatched as 0

# Function to search the keyword in a given directory
def search_files(keyword, path):
    filesFound = filesMatched
    for root, dirs, files in os.walk(path): # Use os.walk(path) to loop through the given directory and get the root, dirs, and files
        for file in files: # Loop through all the files in the given directory.
            if keyword in file: # Check if keyword is in filename
                filesFound += 1 # Counter to see how many files matched
                print(" "+root+'\\'+str(file)+"\n") # Print out the full file path for every matching file
                if filesFound == 0: # Check if no files matched the given keyword and print the same
                    print("No matching files in the given directory! Try another directory!")
    # If 1 or more files are matched then print the same
    print(f"No. of files matched: {filesFound}")

try:
    search_files(keyword, path) # Call the function
# Error and Exception Handling
except FileNotFoundError as e:
    print("Error: FileNotFoundError occured! Make sure you entered the correct path and entered it correctly.")
except Exception as e:
    print(f"Error: Some Error occured! \nDetails: {e}")

Enter fullscreen mode Exit fullscreen mode

Moving on to the 2nd Project, which is super simple and easy and not as fun as the previous one.


Project 2


What are we making?

We are going to be making a simple CLI calculator. It will print out the result of the expression passed to it. For e.g. - Input:

C:\Users\username> calc.py 2 +10 * 5

Enter fullscreen mode Exit fullscreen mode

and output:

60

Enter fullscreen mode Exit fullscreen mode

Steps:

  1. In the same directory as before, create a new file named calc.py and open it.

Actual Code

I am just gonna give the full code and explain it.

# calc.py - CLI-Utilities - play4Tutorials.hashnode.dev
'''
Rules:
1. Do not give any random symbol or alphabet - That gives an error.
2. 1234567890 - Are digits
3. + is addition
4. - is subtraction
5. * is multiplication
6. / is float division(with decimal points)
7. // is integer division(without decimal points)
8. Do not give spaces between two digits of a number - That gives an error
9. Do not run this file by double clicking, Use the command prompt or terminal to run it.
'''
try:
    import sys # stdlib
# Exception and Error Handling
except ImportError as e:
    print("Module - 'sys' could not be imported from stdlib!")

try:
    print(eval(" ".join(sys.argv[1:]))) # Evaluate the given expression and print the result out.
# Exception and Error Handling
except Exception as e:
    print("Expression could not be validated or evaluated.")
    print("1. Make sure that you typed the expression correctly.")
    print("2. Make sure that there are no spaces between 2 digits of a number.")
    print("And try again.")
    print(f"Details about the error: {e}")

Enter fullscreen mode Exit fullscreen mode

Since you already know what sys.argv[] does I am not explaining it once more. And also try/except.

Over here, print(eval()) is being used. So what it does is that it is an in-built function, which evaluates an math expression and prints out the result, very simple.


How to access it from anywhere?


Follow everything given below.

  1. Navigate to the folder where your files are stored using Windows Explorer.
  2. Copy the Path of the Folder. For e.g. - D:\Programming\CLI-Utilities
  3. Open Start Menu and type "system variables" and click on "Edit the system environment variables"
  4. From the new window that pops-up click on Environment Variables
  5. Another new window opens up. With two sections "User variables for username" and "System variables"
  6. Under "system variables", scroll down until you find the "Path" variable and then select it and click edit under it.
  7. From the new small window, under variable value, go to the end.
  8. Put a semicolon at the end if it is not there.
  9. Then paste your copied path there.
  10. Then click ok, again ok, and again ok.
  11. Then open a command prompt window and from any directory type search.py or calc.py and it will work.

Bonus!


If you want it so that you don't have to type the .py extension. I'll post that in another post soon. So make sure to subscribe to the mailing list.


Sorry!


I was busy with unhackable. It recently got an update and a more major one is coming soon. I really need to work on that. So I couldn't post the past months. I am really sorry.


The End

Oldest comments (1)

Collapse
 
n_michael_168ce3daacda46c profile image
N Michael

the if statement:

if filesFound == 0: # Check if no files matched the given keyword and print the same
print("No matching files in the given directory! Try another directory!")

is indented too far, its impossible to reach it