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
and output:
D:\Programming\CLI-Utilities\search.py
D:\Programming\CLI-Utilities\calc.py
D:\Programming\etc.py
No. of files matched = 3
Input:
C:\Users\username> search.py python D:\Programming
and output:
No matching files in the given directory! Try another directory!
Did you see that even though our file is in D:\Programming
, we were access it from C:\Users\username
?
Steps:
- Create a new folder named
cli-utilities
-
cd
into that directory. - Open it in your code editor. Type
code .
to open that directory in VSCode(if you are using VSCode). - Make a new file named
search.py
. - 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
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')")
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
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.
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}")
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):
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!")
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}")
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}")
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}")
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
and output:
60
Steps:
- 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}")
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.
- Navigate to the folder where your files are stored using Windows Explorer.
- Copy the Path of the Folder. For e.g. - D:\Programming\CLI-Utilities
- Open Start Menu and type "system variables" and click on "Edit the system environment variables"
- From the new window that pops-up click on Environment Variables
- Another new window opens up. With two sections "User variables for username" and "System variables"
- Under "system variables", scroll down until you find the "Path" variable and then select it and click edit under it.
- From the new small window, under variable value, go to the end.
- Put a semicolon at the end if it is not there.
- Then paste your copied path there.
- Then click ok, again ok, and again ok.
- Then open a command prompt window and from any directory type
search.py
orcalc.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.
Top comments (1)
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