https://github.com/elo-enterprises/shil
Parser/formatter/pprinter for shell code
https://github.com/elo-enterprises/shil
python shell
Last synced: 29 days ago
JSON representation
Parser/formatter/pprinter for shell code
- Host: GitHub
- URL: https://github.com/elo-enterprises/shil
- Owner: elo-enterprises
- License: mit
- Created: 2023-06-07T21:20:27.000Z (about 3 years ago)
- Default Branch: master
- Last Pushed: 2025-08-06T05:19:53.000Z (11 months ago)
- Last Synced: 2026-05-17T17:32:46.662Z (about 2 months ago)
- Topics: python, shell
- Language: Python
- Homepage:
- Size: 172 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
shil

Shell-util library for python.
Includes helpers for subprocess invocation, shell-formatters / pretty-printers, and more.
---------------------------------------------------------------------------------
- Overview
-
Features
- Installation
- Usage (CLI)
-
Usage (API)
- OOP-style Dispatch
- Functional approach to dispatch
- Loading data when command-output is JSON
- Serialization with Pydantic
- Caller determines logging
- Rich-console Support
- Stay DRY with Runners
---------------------------------------------------------------------------------
## Overview
The `shil` library provides various shell-utilities for python.
-------------------------------------------------------------------------------
## Features
### Shell-formatters / pretty-printers
* A somewhat usable parser / grammar for bash
* Console support for [rich](https://rich.readthedocs.io/en/stable/index.html) & [rich protocols](https://rich.readthedocs.io/en/stable/protocol.html)
### Subprocess Invocation
There's a lot of shell-related libraries out there, especially for invocation (see for example [this list](https://www.pyinvoke.org/prior-art.html)).
The interface for `shil` is hopefully unsurprising, and something that's convenient but not fancy. It's a utility, and not a huge framework.
The main goal is to provide an API that is simple and stable, without a ton of dependencies.
```pycon
>>> import shil
>>> proc = shil.invoke('echo hello world')
>>> assert proc.succeeded
>>> assert proc.stdout.strip()=='hello world'
>>>
```
Beyond such basics, shil includes support for [rich output](#) and uses pydantic for datastructures.
See the [API docs](#usage-api) for more detailed information.
---------------------------------------------------------------------------------
## Installation
See [pypi](https://pypi.org/project/shil/) for available releases.
```bash
$ pip install shil
```
---------------------------------------------------------------------------------
## Usage (CLI)
The shil library publishes a small CLI tool, mostly just for testing & demoing the API behaviour. See [the CLI docs](docs/cli/) for the latest (autogenerated) help.
-------------------------------------------------------------------------------
## Usage (API)
See also:
* [the unit-tests](tests/units) for some examples of library usage
* [the smoke-tests](tests/smoke/test.sh) for example usage of stand-alone scripts
### OOP-style Dispatch
This uses `shil.Invocation` and returns `shil.InvocationResponse`.
```pycon
>>> import shil
>>> req = cmd = shil.Invocation(command='printf hello-world\n')
>>> resp = cmd()
>>> print(resp.stdout)
hello-world
>>>
```
### Functional approach to dispatch
Use `shil.invoke`, get back `shil.InvocationResponse`
```pycon
>>> import shil
>>> resp = shil.invoke(command='printf hello-world\n')
>>> print(resp.stdout)
hello-world
>>>
```
### Loading data when command-output is JSON
```pycon
>>> import shil
>>> cmd = shil.Invocation(command="""echo '{"foo":"bar"}'""", load_json=True)
>>> resp = cmd()
>>> print(resp.data)
{'foo': 'bar'}
>>> assert type(resp.data) == type({})
>>> assert resp.data['foo'] == 'bar'
>>>
```
### Serialization with Pydantic
```pycon
>>> import json, shil
>>> req = cmd = shil.Invocation(command="""echo pipes-are-allowed|grep allowed""")
>>> resp = cmd()
>>> keys = resp.dict().keys()
>>> expected = 'stdout stderr failed failure success succeeded data'.split()
>>> assert all([k in keys for k in expected])
>>>
```
### Caller determines logging
Works like this with basic logger:
```pycon
>>> import logging, shil
>>> logger = logging.getLogger()
>>> resp = shil.invoke('ls /tmp', command_logger=logger.critical, output_logger=logger.warning)
>>>
```
Supports using [rich-logger](https://rich.readthedocs.io/en/stable/logging.html) too:
```pycon
>>> import shil
>>> from rich.console import Console
>>> console = Console(stderr=True)
>>> resp = shil.invoke('ls /tmp', command_logger=console.log)
>>>
```
### Rich-console Support
Besides using rich-logger as above, you can use the [rich-protocol](https://rich.readthedocs.io/en/stable/protocol.html) more directly.
Printing works the way you'd expect for `Invocation` and `InvocationResponse`.
```pycon
>>> import shil, rich
>>> req = cmd = shil.Invocation(command='echo {"foo":"bar"}')
>>> resp = cmd()
>>> rich.print(req)
>>> rich.print(resp)
```
By default, output looks roughly like this:


### Stay DRY with Runners
Runner's are basically just [partials](https://en.wikipedia.org/wiki/Partial_application) on `shil.invoke`. It's simple but this can help reduce copying around repetitive configuration.
```pycon
>>> import shil
>>> from rich.console import Console
>>> console=Console(stderr=True)
>>> runner = shil.Runner(output_logger=console.log, command_logger=console.log)
>>> resp = runner('ls /tmp')
>>> assert isinstance(resp,(shil.InvocationResult,))
>>>
```