Dan Bader

How do I make my own command line commands using Python?

The Python script you just wrote would make a great little command line tool – but having to type “python myscript.py” all the time to launch your program gets daunting fast. Here’s how you can make your Python script feel like a real shell command.

At the end of this short tutorial you’ll know how to write a Python program that simulates the Unix echo command and can be started from the command line just like it:

$ myecho Hello, World!

Sweet, let’s jump right in!

Imagine you have the following short Python script called myecho.py that just prints the command line arguments you pass to it back to the console:

import sys

for arg in sys.argv:
    print(arg)

You can run this command just fine by passing it to the Python interpreter like so:

$ python myecho.py Hello, World!
myecho.py
Hello,
World!

But how can you give your users a more polished experience that allows them simply type myecho Hello, World! and get the same result?

Easy – there are three things you need to do:

Step 1: Mark your Python file as executable

The first thing you’ll need to do is mark your script as executable in the file system, like so:

$ chmod +x myecho.py

This sets the executable flag on myecho.py, which tells the shell that it’s a program that can be run directly from the command line. Let’s try it:

$ ./myecho.py Hello, World!

We need to prefix our command with ./ because usually the current directory is not included in the PATH environment variable on Unix. This is a security feature1.

Anyway – the result will be that you get a crazy error message when you try to run myecho.py. It’ll probably look something like this:

./myecho.py: line 4: syntax error near unexpected token `print'
./myecho.py: line 4: `    print(arg)'

The reason for that is that now the system doesn’t know it’s supposed to execute a Python script. So instead it takes a wild guess and tries to run your Python script like a shell script with the /bin/sh interpreter.

That’s why you’re getting these odd syntax errors. But there’s an easy fix for this in the next step. You just need to …

Step 2: Add an interpreter “shebang”

Okay, admittedly this sounds completely crazy if you’ve never heard of Unix shebangs before…😃 But it’s actually a really simple concept and super useful:

Whenever you run a script file on an Unix-like operating system (like Linux or macOS) the program loader responsible for loading and executing your script checks the first line for an interpreter directive. Here’s an example:

#!/bin/sh

You’ve probably seen those before. These interpreter directives are also called shebangs in Unix jargon2. They tell the program loader which interpreter should execute the script.

You can use this mechanism to your advantage by adding a shebang line that points to the system Python interpreter:

#!/usr/bin/env python

You may be wondering why you should be using env to load the Python interpreter instead of simply using an absolute path like /usr/local/bin/python.

The reason for that is that the Python interpreter will be installed in different locations on different systems. On a Mac using Homebrew it might be in /usr/local/bin/python. On a Ubuntu Linux box it might be in /usr/bin/python.

Using another level of indirection through env you can select the Python interpreter that’s on the PATH environment variable. That’s usually the right way to go about it3.

Okay, so now that you’ve added that #!/usr/bin/env python line your script should look like this:

#!/usr/bin/env python
import sys

for arg in sys.argv:
    print(arg)

Let’s try to run it again!

$ ./myecho.py Hello, World!
./myecho.py
Hello,
World!

Yes! Success!

Now that you’re using the interpreter directive shebang in the script you can also drop the .py extension. This will make your script look even more like a system tool:

$ mv myecho.py myecho

This is starting to look pretty good now:

$ ./myecho Hello, World!
./myecho
Hello,
World!

Step 3: Make sure your program is on the PATH

The last thing you need to change to make your Python script really seem like a shell command or system tool is to make sure it’s on your PATH.

That way you’ll be able to launch it from any directory by simply running myecho Hello, World!, just like the “real” echo command.

Here’s how to achieve that.

I don’t recommend that you try to copy your script to a system directory like /usr/bin/ or /usr/local/bin because that can lead to all kinds of odd naming conflicts (and, in the worst case, break your operating system install).

So instead, what you’ll want to do is to create a bin directory in your user’s home directory and then add that to the PATH.

First, you need to create the ~/bin directory:

$ mkdir -p ~/bin

Next, copy your script to ~/bin:

$ cp myecho ~/bin

Finally, add ~/bin to your PATH:

export PATH=$PATH":$HOME/bin"

Adding ~/bin to the PATH like this is only temporary, however. It won’t stick across terminal sessions or system restarts. If you want to make your command permanently available on a system, do the following:

  • Add the this line to .profile or .bash_profile in your home directory: export PATH=$PATH":$HOME/bin".
  • You can either use an editor to do it or run the following command to do that: echo 'export PATH=$PATH":$HOME/bin"' >> .profile
  • Changes to .profile or .bash_profile only go into effect when your shell reloads these files. You can trigger a reload by either opening a new terminal window or running this command: source .profile

Okay great, now we finally get the result we wanted all along:

$ myecho Hello, World!
/Users/youruser/bin/myecho
Hello,
World!

Whew. When I write these tutorials I’m always surprised how much work seemingly “simple” things take when you’re trying to get to the bottom of them. So don’t be too hard on yourself if some of these steps felt a little arcane at first 😃. All of this stuff becomes second nature once you’ve dealt with it a few times.


  1. Here’s an awesome in-depth explanation for this security feature and why it is necessary: www.linfo.org/dot_slash.html 

  2. You can read all about Unix shebangs here: wikipedia.org/wiki/Shebang_(Unix) 

  3. Learn more about env and its merits here: wikipedia.org/wiki/Env 

Improve Your Python with a fresh 🐍 Python Trick 💌 every couple of days

🔒 No spam ever. Unsubscribe any time.

This article was filed under: programming, and python.

Read next:
  • A countdown timer extension for Alfred – I wrote a countdown timer extension for the Alfred application launcher for OS X. The extension is open-source, written in Python and uses Mountain Lion’s user notifications.
  • Monochrome font rendering with FreeType and Python – For my Raspberry Pi internet radio project I needed a way to render text suitable for a low resolution monochrome LCD. This article describes how to render 1-bit text using FreeType and Python.
  • Setting up Sublime Text for Python development – I recently started using Sublime Text 2 more and more as my main editor for Python development. This article explains my setup and some tweaks that make Python programmers happy.
  • Functional linked lists in Python – Linked lists are fundamental data structures that every programmer should know. This article explains how to implement a simple linked list data type in Python using a functional programming style.
  • Abstract Base Classes in Python – Abstract Base Classes (ABCs) enforce that derived classes implement particular methods from the base class. In this chapter you’ll learn about the benefits of abstract base classes and how to define them with Python’s built-in “abc” module.
← More articles