• Stars
    star
    650
  • Rank 69,267 (Top 2 %)
  • Language
    Python
  • License
    MIT License
  • Created over 3 years ago
  • Updated 2 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

yen

The last Python environment manager you'll ever need.
Python
211
star
2

packaged

The easiest way to ship python applications.
Shell
191
star
3

cursed-for

Python
92
star
4

piston_bot

A Telegram bot that will run code for you.
Go
49
star
5

python_leetcode_runner

Test your leetcode Python solutions locally.
Python
40
star
6

recv

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

zigimports

Automatically remove unused imports and globals from Zig files.
Zig
30
star
8

blog

The blog
Astro
26
star
9

interpreted

A Python interpreter, in Python.
Python
24
star
10

instachat

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

json5kit

A Parser and CST for JSON5.
Python
21
star
12

url-shortener

My free URL shortener hack
Python
19
star
13

flutter_sidebar

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

python-starter

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

scaffold_responsive

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

leetcode

My leetcode solutions
Python
8
star
17

astest

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

sanskrit

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

pycify

Convert your entire Python project from .py files to .pyc files.
Python
6
star
20

tushar.bio

The Website
JavaScript
5
star
21

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
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
5
star
23

daily_byte

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

drawer_3d

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

lireddit

A reddit clone
TypeScript
4
star
26

450-leetcode-questions

The list of 450 questions from love babbar - leetcode edition
4
star
27

zlox

Lox bytecode interpreter, written in Zig 0.13.0.
Zig
4
star
28

dunderhell

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

mines

Beautiful implementation of minesweeper in Flutter
Dart
3
star
30

mylint

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

nozomi

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

nictovision

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

pythonplusplus

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

chararray

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

json_parser

An efficient(-ish) JSON parser written in Python.
Python
3
star
36

pylox

My first implementation of Lox, written in Python.
Python
2
star
37

morsedecode

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

strings.py

Print all strings and f-string constants present in a python project.
Python
2
star
39

flappybird

Flappy bird, but every sprite has its own window.
Python
2
star
40

suspense

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

indentify

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

notes

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

astmath

Evaluate Python arithemetic expressions safely.
Python
2
star
44

python_snippets

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

sineline

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

formatjson

Format JSON files
Rust
2
star
47

tic.rs

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

programmers-dice

Generates excuses for programmers.
Python
2
star
49

flutter_animated_profile_page

Dart
1
star
50

steganography

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

mypybook.com

HTML
1
star
52

chat_app

simple chat app written in flutter
Dart
1
star
53

2doku

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

dotfiles

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

oneliners

Python
1
star
56

mypy_workshop

Python
1
star
57

resume

My Resume Template
TeX
1
star
58

emojy

Write Python🐍 with emojis✨
Python
1
star
59

ordinal

Get ordinals from numbers
Python
1
star
60

vscvm

A VSCode version manager.
Python
1
star
61

blurred

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

free-threading-talk

Python
1
star
63

zyp

Zig
1
star