• Stars
    star
    641
  • Rank 68,502 (Top 2 %)
  • Language
    Python
  • License
    MIT License
  • Created about 3 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Shell scripts made simple 🐚

zxpy

Downloads Code style: Black CI Status

Shell scripts made simple 🐚

zxpy lets you seamlessly write shell commands inside Python code, to create readable and maintainable shell scripts.

Inspired by Google's zx, but made much simpler and more accessible using Python.

Rationale

Bash is cool, and it's extremely powerful when paired with linux coreutils and pipes. But apart from that, it's a whole another language to learn, and has a (comparatively) unintuitive syntax for things like conditionals and loops.

zxpy aims to supercharge bash by allowing you to write scripts in Python, but with native support for bash commands and pipes.

Let's use it to find all TODOs in one of my other projects, and format them into a table:

#! /usr/bin/env zxpy
todo_comments = ~"git grep -n TODO"
for todo in todo_comments.splitlines():
    filename, lineno, code = todo.split(':', 2)
    *_, comment = code.partition('TODO')
    print(f"{filename:40} on line {lineno:4}: {comment.lstrip(': ')}")

Running this, we get:

$ ./todo_check.py
README.md                                on line 154 : move this content somewhere more sensible.
instachat/lib/models/message.dart        on line 7   : rename to uuid
instachat/lib/models/update.dart         on line 13  : make int
instachat/lib/services/chat_service.dart on line 211 : error handling
server/api/api.go                        on line 94  : move these to /chat/@:address
server/api/user.go                       on line 80  : check for errors instead of relying on zero value

Writing something like this purely in bash or in Python would be much harder than this. Being able to use linux utilities seamlessly with a readable, general purpose language is what makes this a really powerful tool.

A larger, practical example

You can find a comparison between a practical-ish script written in bash and zxpy in EXAMPLE.md

Installation

pip install zxpy

pipx

If you have pipx installed, you can try out zxpy without installing it, by running:

pipx run zxpy

Basic Examples

Make a file script.py (The name and extension can be anything):

#! /usr/bin/env zxpy
~'echo Hello world!'

file_count = ~'ls -1 | wc -l'
print("file count is:", file_count)

And then run it:

$ chmod +x ./script.py

$ ./script.py
Hello world!
file count is: 3

Run >>> help('zx') in Python REPL to find out more ways to use zxpy.

A slightly more involved example: run_all_tests.py

#! /usr/bin/env zxpy
test_files = (~"find -name '*_test\.py'").splitlines()

for filename in test_files:
    try:
        print(f'Running {filename:.<50}', end='')
        output = ~f'python {filename}'  # variables in your shell commands :D
        assert output == ''
        print('Test passed!')
    except:
        print(f'Test failed.')

Output:

$ ./run_all_tests.py
Running ./tests/python_version_test.py....................Test failed.
Running ./tests/platform_test.py..........................Test passed!
Running ./tests/imports_test.py...........................Test passed!

More examples are in EXAMPLE.md, and in the examples folder.

stderr and return codes

To get stderr and return code information out of the shell command, there is an alternative way of invoking the shell.

To use it, just use 3 variables on the left side of your ~'...' shell string:

stdout, stderr, return_code = ~'echo hi'
print(stdout)       # hi
print(return_code)  # 0

More examples are in the examples folder.

CLI Arguments

When writing a shell script, you often want to pass CLI arguments to it.

Like so:

$ cat ./foo.sh
echo arg is: $1

$ ./foo.sh 123
arg is: 123

To do the same in zxpy, pass the script arguments after a -- in the zxpy CLI command.

#!/usr/bin/env zxpy

import sys
print("Argv is:", sys.argv)

~"echo output: $1 $2 $3"
$ ./test.py
Argv is: ['/bin/sh']
output:

$ ./test.py -- abc def
Argv is: ['/bin/sh', 'abc', 'def']
output: abc def

Both $1 and sys.argv[1] will do the same thing.

Quoting

Take this shell command:

$ uname -a
Linux pop-os 5.11.0 [...] x86_64 GNU/Linux

Now take this piece of code:

>>> cmd = 'uname -a'
>>> ~f'{cmd}'
/bin/sh: 1: uname -a: not found

Why does this not work?

This is because uname -a was quoted into 'uname -a'. All values passed inside f-strings are automatically quoted to avoid shell injection.

To prevent quoting, the :raw format_spec can be used:

>>> cmd = 'uname -a'
>>> ~f'{cmd:raw}'
Linux pop-os 5.11.0 [...] x86_64 GNU/Linux

This disables quoting, and the command is run as-is as provided in the string.

Note that this shouldn't be used with external data, or this will expose you to shell injection.

Interactive mode

$ zxpy
zxpy shell
Python 3.8.5 (default, Jan 27 2021, 15:41:15)
[GCC 9.3.0]

>>> ~"ls | grep '\.py'"
__main__.py
setup.py
zx.py
>>>

Also works with path/to/python -m zx

It can also be used to start a zxpy session in an already running REPL. Simply do:

>>> import zx; zx.install()

and zxpy should be enabled in the existing session.

Development/Testing

To install from source, clone the repo, and do the following:

$ source ./venv/bin/activate  # Always use a virtualenv!
$ pip install -r requirements-dev.txt
Processing ./zxpy
[...]
Successfully installed zxpy-1.X.X
$ pytest  # runs tests

More Repositories

1

cursed-for

Python
93
star
2

piston_bot

A Telegram bot that will run code for you.
Go
46
star
3

python_leetcode_runner

Test your leetcode Python solutions locally.
Python
40
star
4

yen

Create virtual environments for any Python version, without needing Python installed.
Rust
38
star
5

recv

A super simple, super fast way to share files, links and text between devices.
Go
33
star
6

blog

The blog
Astro
24
star
7

interpreted

A Python interpreter, in Python.
Python
21
star
8

instachat

A working replica of Instagram DMs and stories, written in Flutter and Go.
Go
21
star
9

json5kit

A Parser and CST for JSON5.
Python
20
star
10

url-shortener

My free URL shortener hack
Python
17
star
11

flutter_sidebar

An easy to configure sidebar widget for your flutter mobile/web apps
Dart
15
star
12

python-starter

A straightforward starter template for Python packages.
Python
11
star
13

scaffold_responsive

A responsive scaffold widget that adjusts to your device size, for your flutter mobile and web apps.
Dart
10
star
14

leetcode

My leetcode solutions
Python
8
star
15

astest

The quick, easy way to write tests in Python.
Python
7
star
16

sanskrit

Write programs in the fastest language, as confirmed by NASA.
Python
6
star
17

tushar.bio

The Website
JavaScript
5
star
18

asd

A place to keep all the test codes I make that don't deserve their own repository but might come handy someday.
Python
5
star
19

daily_byte

My solutions to the email problems sent by The Daily Byte, written in Python.
Python
5
star
20

drawer_3d

A 3D App Drawer implementation in flutter, inspired by https://fidev.io/complex-ui/
Dart
4
star
21

lireddit

A reddit clone
TypeScript
4
star
22

flutter_highlight

A fork of https://github.com/git-touch/highlight, with only the flutter_highlight package, created to add editable highlight views.
Dart
4
star
23

dunderhell

Turn your Python code entirely into dunders.
Python
4
star
24

nictovision

ML-based webapp that brightens dark .DNG images without introducing noise
Python
3
star
25

mylint

My really simple rendition of how a linter works.
Python
3
star
26

mines

Beautiful implementation of minesweeper in Flutter
Dart
3
star
27

450-leetcode-questions

The list of 450 questions from love babbar - leetcode edition
3
star
28

nozomi

A telegram bot that echoes any media that was forwarded from a channel.
Go
3
star
29

pythonplusplus

It's Python, but better.
Python
3
star
30

chararray

Standard mutable string (character array) implementation for Python.
Python
3
star
31

pycify

Convert your entire Python project from .py files to .pyc files.
Python
3
star
32

morsedecode

A morse code encoder and decoder utility.
Python
2
star
33

indentify

Format any code-like snippet in an indented style.
Python
2
star
34

suspense

A React-like Suspense implementation for Flutter.
Dart
2
star
35

notes

Note taking and publishing app made in Flutter
Dart
2
star
36

astmath

Evaluate Python arithemetic expressions safely.
Python
2
star
37

python_snippets

A gallery of runnable python snippets, written in Flutter.
Dart
2
star
38

sineline

A lightweight remake of Android game "Sine Line" from Third State Studio.
JavaScript
2
star
39

json_parser

An efficient(-ish) JSON parser written in Python.
Python
2
star
40

programmers-dice

Generates excuses for programmers.
Python
2
star
41

steganography

A steganographic image encoder and decoder written in Python3.6
Python
1
star
42

flutter_animated_profile_page

Dart
1
star
43

mypybook.com

HTML
1
star
44

pylox

My first implementation of Lox, written in Python.
Python
1
star
45

chat_app

simple chat app written in flutter
Dart
1
star
46

2doku

A two-player sudoku game made using flutter and firebase
Dart
1
star
47

dotfiles

Place to keep all my windows/linux dotfiles
Shell
1
star
48

blurred

An easy way to blur your flutter widgets and images.
Dart
1
star
49

oneliners

Python
1
star
50

mypy_workshop

Python
1
star
51

resume

My Resume Template
TeX
1
star
52

emojy

Write Python🐍 with emojis✨
Python
1
star
53

ordinal

Get ordinals from numbers
Python
1
star
54

vscvm

A VSCode version manager.
Python
1
star
55

formatjson

Format JSON files
Rust
1
star
56

free-threading-talk

Python
1
star
57

tic.rs

Tic Tac Toe written in ~100 lines of idiomatic Rust.
Rust
1
star