Sling Academy
Home/Python/Python asyncio program to run a shell command and get the result

Python asyncio program to run a shell command and get the result

Last updated: February 12, 2024

Overview

In the era of concurrent programming in Python, the asyncio module stands out as a robust cornerstone for writing asynchronous applications. This tutorial unlocks the potential of running shell commands within a Python asyncio program, capturing their output effectively. Through practical examples, you’ll grasp how to integrate shell execution into your asyncio-driven Python applications.

Understanding asyncio

Before diving into code, let’s briefly touch on what asyncio is. asyncio is a Python library introduced in Python 3.4 to write concurrent code using the async/await syntax. It provides the framework for dealing with asynchronous I/O, event loops, and coroutines, paving the way for non-blocking code execution.

Setting Up Your Environment

To follow along with this tutorial, ensure you have a Python version 3.7 or above, as we’ll utilize the enhanced asyncio syntax introduced in Python 3.7. Use the command python --version to verify your Python version.

Running a Shell Command

Running a shell command asynchronously is straightforward with asyncio. We’ll start with the basic structure for running a simple command: ls, which lists the contents of a directory.

import asyncio

async def run_shell_command(cmd):
    process = await asyncio.create_subprocess_shell(cmd,
                                                    stdout=asyncio.subprocess.PIPE,
                                                    stderr=asyncio.subprocess.PIPE)
    stdout, stderr = await process.communicate()
    if stderr:
        print(f"Error: {stderr.decode()}")
    print(f"Output: {stdout.decode()}")

asyncio.run(run_shell_command('ls'))

This asynchronous function run_shell_command creates a subprocess for the shell command using asyncio.create_subprocess_shell and awaits its completion. The output and possible error messages are captured and decoded for display.

Enhancing the Functionality

Let’s extend the functionality of our function to accommodate multiple commands and include real-time output streaming.

async def run_multiple_commands(commands):
    for cmd in commands:
        process = await asyncio.create_subprocess_shell(cmd,
                                                        stdout=asyncio.subprocess.PIPE,
                                                        stderr=asyncio.subprocess.PIPE)
        async for line in process.stdout:
            print(f"{cmd}: {line.decode().strip()}")

        await process.wait()

asyncio.run(run_multiple_commands(['ls', 'whoami']))

This iteration allows us to execute multiple shell commands sequentially, printing their outputs in real-time. The async for loop reads the process’s stdout line by line, offering a glimpse into stream processing with asyncio.

Executing Commands Concurrently

Running commands sequentially might not be efficient for all use cases. Let’s think asynchronously and run multiple commands concurrently.

async def run_concurrently(*cmds):
    tasks = [run_shell_command(cmd) for cmd in cmds]
    await asyncio.gather(*tasks)

asyncio.run(run_concurrently('ls', 'whoami', 'uname -a'))

Here, we define a list of tasks, each executing a shell command through our previously defined run_shell_command function. asyncio.gather is then used to run these tasks concurrently, showcasing the power of asyncio in handling multiple asynchronous operations simultaneously.

Handling Errors

Handling errors is crucial when running shell commands. Our initial example included basic error checking. For a more robust solution, you might want to implement exception handling:

async def run_shell_command(cmd):
    try:
        process = await asyncio.create_subprocess_shell(cmd,
                                                        stdout=asyncio.subprocess.PIPE,
                                                        stderr=asyncio.subprocess.PIPE)
        stdout, stderr = await process.communicate()
        if stderr:
            print(f"Error: {stderr.decode()}")
        print(f"Output: {stdout.decode()}")
    except Exception as e:
        print(f"Execution failed: {e}")

This approach ensures that our program remains robust and less prone to crash due to unexpected errors during the execution of shell commands.

Conclusion

The asyncio module enhances Python with the ability to write efficient and scalable asynchronous code. Running shell commands within this environment unlocks a wealth of possibilities for automation, data processing, and system management tasks. This tutorial provided a comprehensive overview of executing shell commands asynchronously with asyncio, from basic one-off commands to running multiple commands concurrently. Armed with these techniques, you can now integrate shell command execution into your asyncio-based Python projects with ease.

Next Article: Python asyncio: What is a subprocess and how to use it

Previous Article: Exploring asyncio.Event in Python (through examples)

Series: Python Asynchronous Programming Tutorials

Python

You May Also Like

  • Python Warning: Secure coding is not enabled for restorable state
  • Python TypeError: write() argument must be str, not bytes
  • 4 ways to install Python modules on Windows without admin rights
  • Python TypeError: object of type ‘NoneType’ has no len()
  • Python: How to access command-line arguments (3 approaches)
  • Understanding ‘Never’ type in Python 3.11+ (5 examples)
  • Python: 3 Ways to Retrieve City/Country from IP Address
  • Using Type Aliases in Python: A Practical Guide (with Examples)
  • Python: Defining distinct types using NewType class
  • Using Optional Type in Python (explained with examples)
  • Python: How to Override Methods in Classes
  • Python: Define Generic Types for Lists of Nested Dictionaries
  • Python: Defining type for a list that can contain both numbers and strings
  • Using TypeGuard in Python (Python 3.10+)
  • Python: Using ‘NoReturn’ type with functions
  • Type Casting in Python: The Ultimate Guide (with Examples)
  • Python: Using type hints with class methods and properties
  • Python: Typing a function with default parameters
  • Python: Typing a function that can return multiple types