{"id":2266,"library":"runs","title":"Runs","description":"Runs is a Python library (version 1.3.0) that enhances the standard `subprocess` module by providing improved functions to execute blocks of text as sequences of shell commands. It adds features like multi-command execution, line continuations, comment handling, optional logging, error handling, lazy evaluation, and defaults to UTF-8 encoding. It offers more robust handling for scenarios that often trip up the native `subprocess` functions.","status":"active","version":"1.3.0","language":"en","source_language":"en","source_url":"https://github.com/rec/runs/","tags":["subprocess","shell","cli","automation","command-execution"],"install":[{"cmd":"pip install runs","lang":"bash","label":"Install stable version"}],"dependencies":[],"imports":[{"note":"Used for executing commands and waiting for completion, returning a list of results.","symbol":"run","correct":"from runs import run"},{"note":"Equivalent to subprocess.call() but for multiple commands/text blocks.","symbol":"call","correct":"from runs import call"},{"note":"Similar to subprocess.check_call(), raises CalledProcessError on non-zero exit codes.","symbol":"check_call","correct":"from runs import check_call"},{"note":"Similar to subprocess.check_output(), captures output and raises CalledProcessError on failure.","symbol":"check_output","correct":"from runs import check_output"}],"quickstart":{"code":"from runs import run\n\n# Run a block of text as multiple commands\noutput = run('''\n  echo \"Hello from Runs!\"\n  ls -l\n''')\n\nfor line in output:\n    print(line)\n\n# Run with error handling and echo\ntry:\n    result = run('false; echo ok', on_exception=False, echo=True)\nexcept Exception as e:\n    print(f\"Caught expected error: {e}\")\n\n# Get stdout of a single command\nstdout_list = run('echo \"Single command output\"')\nprint(stdout_list[0])","lang":"python","description":"This example demonstrates how to use `runs.run()` to execute a multi-line block of commands and capture their output. It also shows how to enable echoing commands and handle exceptions from failing subprocesses. The library provides a more convenient interface for common shell interactions than the raw `subprocess` module."},"warnings":[{"fix":"Use explicit line continuations for multi-line shell commands within a single text block, or pass commands as a list of strings if precise command separation is critical, e.g., `runs.run(['command arg1 arg2', 'another_command'])`.","message":"By default, the `runs` library splits input text into commands by newlines. Ensure that multi-line commands intended as a single unit use line continuations (e.g., `\\`) as expected by your shell, or pass commands as a list of strings.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Avoid constructing command strings with unsanitized user input. If user input must be part of a command, pass the command and its arguments as a list of strings (where each element is an argument) rather than a single string, to bypass shell interpretation. Alternatively, use `shlex.quote()` to properly escape individual arguments.","message":"While `runs` aims to improve `subprocess` error handling, raw shell commands executed with `shell=True` (which `runs` implies when passing a single string block) are susceptible to shell injection if untrusted input is included. Always sanitize or escape user-provided data.","severity":"gotcha","affected_versions":"All versions"},{"fix":"For asynchronous or parallel execution, consider directly using `subprocess.Popen` or dedicated async/parallel processing libraries like `asyncio.create_subprocess_exec` or `multiprocessing`.","message":"The `runs` library processes commands sequentially. For long-running or concurrent tasks, using `runs` might block your main program. It does not inherently provide asynchronous execution or process management capabilities beyond waiting for completion.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Adjust your code to iterate over the returned list if you expect multiple outputs, or access `result[0]` if only the first command's output is relevant. Remember that each element in the list is the `stdout` of a command, not a `CompletedProcess` object.","message":"The library's functions (e.g., `run()`, `check_output()`) return a list of strings, one for each command executed in the input block. This differs from `subprocess.run()` which returns a single `CompletedProcess` object.","severity":"breaking","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-09T00:00:00.000Z","next_check":"2026-07-08T00:00:00.000Z"}