{"id":18875389,"url":"https://github.com/contextualist/shx","last_synced_at":"2025-07-20T10:34:29.505Z","repository":{"id":57467000,"uuid":"366847056","full_name":"Contextualist/shx","owner":"Contextualist","description":"Enjoyable scripting experience with Python","archived":false,"fork":false,"pushed_at":"2024-04-06T20:28:25.000Z","size":26,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-22T07:41:29.751Z","etag":null,"topics":["asyncio","python","shell-scripting"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Contextualist.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-05-12T20:35:33.000Z","updated_at":"2024-01-15T13:28:48.000Z","dependencies_parsed_at":"2024-04-06T21:28:19.281Z","dependency_job_id":null,"html_url":"https://github.com/Contextualist/shx","commit_stats":{"total_commits":17,"total_committers":1,"mean_commits":17.0,"dds":0.0,"last_synced_commit":"82b92fa6f06afd89ff698691c567bff37d1074d9"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/Contextualist/shx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Contextualist%2Fshx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Contextualist%2Fshx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Contextualist%2Fshx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Contextualist%2Fshx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Contextualist","download_url":"https://codeload.github.com/Contextualist/shx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Contextualist%2Fshx/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266111439,"owners_count":23877980,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asyncio","python","shell-scripting"],"created_at":"2024-11-08T06:07:14.995Z","updated_at":"2025-07-20T10:34:29.177Z","avatar_url":"https://github.com/Contextualist.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# shx\n\n[![PyPI version](https://img.shields.io/pypi/v/shx.svg)](https://pypi.org/project/shx)\n\n\u003e Inspired by [zx](https://github.com/google/zx)\n\n```python\n#!/usr/bin/env shx\n\nawait $\"cat setup.py | grep name\"\n\nbranch = await $(\"git branch --show-current\", capture='o')\nawait $f\"dep deploy --branch={branch}\"\n\nawait gather(\n  $\"sleep 1; echo 1\",\n  $\"sleep 2; echo 2\",\n  $\"sleep 3; echo 3\",\n)\n\nname = \"foo bar\"\nawait $f\"mkdir /tmp/{Q(name)}\"\n```\n\n(Take a look at [more examples](examples.md).)\n\n`shx` makes your script writing experience better by taking the advantages of Python's sugary syntax, AsyncIO, and the extensive Python ecosystem. `shx` does three things:\n\n1. Wrap `asyncio.create_subprocess_shell` around with a [syntax sugar](#about-the-subprocess-syntax). `await $\"command\"` returns an [`asyncio.subprocess.Process`](https://docs.python.org/3/library/asyncio-subprocess.html#asyncio.asyncio.subprocess.Process) instance; on non-zero return code, raise [`subprocess.CalledProcessError`](https://docs.python.org/3/library/subprocess.html#subprocess.CalledProcessError).\n2. Provide a top-level async environment.\n3. Preload commonly used imports and utilities. Currently, the imports are:\n\n```Python\nimport asyncio\nfrom asyncio import *\nfrom pathlib import Path\nfrom shlex import quote as Q\nimport shutil\n```\n\n\u003e Note that `shx` does not perform quote escape automatically. Use function `Q` (alias of [`shlex.quote`](https://docs.python.org/3/library/shlex.html#shlex.quote)) to escape unsafe arguments.\n\n## Install\n\n```bash\npip install shx\n```\n\n## Settings and utility functions\n\nSettings can either be [task local](contextvars.md) (e.g. `__.trace = True`) or per-command (e.g. `await $(\"echo 42\", trace=True)`):\n\n* `shell` (Default: `$(which bash)`): Shell to be used.\n* `prefix` (Default: `set -euo pipefail;`): String to be prepended to a command.\n* `trace` (Default: `True`): Display command if set to True. Same as `set -x` in bash.\n* `capture` (Default: `False`): If set to True, capture stdout and stderr instead of displaying them. The captured strings will replace the `.stdout` and `.stderr` attributes of the `asyncio.subprocess.Process` instance returned. `await $(\"...\", capture='o')` and `await $(\"...\", capture='e')` are the aliases of `(await $(\"...\", capture=True)).stdout` and `(await $(\"...\", capture=True)).stderr`, respectively.\n\nAttributes:\n\n* `__.argv`: alias of `sys.argv`, a list of command line arguments\n* `__.env`: alias of `os.environ`, a dict of environment variables\n\n### `cd(cwd: str)`\n\nChange working directory to `cwd`. Same as the task local settings, the changes are only effective within the current task.\n\n### `question(prompt: str)`\n\n[`input()`](https://docs.python.org/3/library/functions.html#input) with `KeyboardInterrupt` handling.\n\n\n## About the subprocess syntax\n\nNo magic, no meta-programming, and no hacking, whatsoever. Prior execution, the script is [tokenized](https://docs.python.org/3/library/tokenize.html), and the following replacements occur:\n\n* \"str prefix\" `$\"command\"` -\u003e `SHX(\"command\")`\n* \"function\" `$(\"command\", k1=v1, ...)` -\u003e `SHX(\"command\", k1=v1, ...)`\n\nwhere `SHX` is an async function wrapping around `asyncio.create_subprocess_shell`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontextualist%2Fshx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcontextualist%2Fshx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontextualist%2Fshx/lists"}