shx
Inspired by zx
#!/usr/bin/env shx
await $"cat setup.py | grep name"
branch = await $("git branch --show-current", capture='o')
await $f"dep deploy --branch={branch}"
await gather(
$"sleep 1; echo 1",
$"sleep 2; echo 2",
$"sleep 3; echo 3",
)
name = "foo bar"
await $f"mkdir /tmp/{Q(name)}"
(Take a look at more examples.)
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:
- Wrap
asyncio.create_subprocess_shellaround with a syntax sugar.await $"command"returns anasyncio.subprocess.Processinstance; on non-zero return code, raisesubprocess.CalledProcessError. - Provide a top-level async environment.
- Preload commonly used imports and utilities. Currently, the imports are:
import asyncio
from asyncio import *
from pathlib import Path
from shlex import quote as Q
import shutil
Note that
shxdoes not perform quote escape automatically. Use functionQ(alias ofshlex.quote) to escape unsafe arguments.
Install
pip install shx
Settings and utility functions
Settings can either be task local (e.g. __.trace = True) or per-command (e.g. await $("echo 42", trace=True)):
shell(Default:$(which bash)): Shell to be used.prefix(Default:set -euo pipefail;): String to be prepended to a command.trace(Default:True): Display command if set to True. Same asset -xin bash.capture(Default:False): If set to True, capture stdout and stderr instead of displaying them. The captured strings will replace the.stdoutand.stderrattributes of theasyncio.subprocess.Processinstance returned.await $("...", capture='o')andawait $("...", capture='e')are the aliases of(await $("...", capture=True)).stdoutand(await $("...", capture=True)).stderr, respectively.
Attributes:
__.argv: alias ofsys.argv, a list of command line arguments__.env: alias ofos.environ, a dict of environment variables
cd(cwd: str)
Change working directory to cwd. Same as the task local settings, the changes are only effective within the current task.
question(prompt: str)
input() with KeyboardInterrupt handling.
About the subprocess syntax
No magic, no meta-programming, and no hacking, whatsoever. Prior execution, the script is tokenized, and the following replacements occur:
- "str prefix"
$"command"->SHX("command") - "function"
$("command", k1=v1, ...)->SHX("command", k1=v1, ...)
where SHX is an async function wrapping around asyncio.create_subprocess_shell.