Shell scripts made simple 🐚

Overview

zxpy

Shell scripts made simple 🐚

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:

#! /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

Installation

pip install zxpy

Example

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 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!

Examples are all in the examples folder.

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.start()

and zxpy should be enabled in the existing session.

Comments
  • shell hangs when `echo` is used

    shell hangs when `echo` is used

    So for the following script

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

    once I executed it, the shell hangs after printing Hello. When I press ctrl+c, I get:

    ^CTraceback (most recent call last):
      File "/usr/local/bin/zxpy", line 8, in <module>
        sys.exit(cli())
      File "/usr/local/lib/python3.7/site-packages/zx.py", line 59, in cli
        run_zxpy(filename, module)
      File "/usr/local/lib/python3.7/site-packages/zx.py", line 136, in run_zxpy
        "$shlex_quote": shlex.quote,
      File "./bug.py", line 3, in <module>
        ~'echo Hello'
      File "/usr/local/lib/python3.7/site-packages/zx.py", line 100, in run_shell_print
        sys.stdout.buffer.write(text)
    KeyboardInterrupt
    
    opened by hacker-DOM 17
  • Problems using a package

    Problems using a package

    Hi there, the following simple script doesn't work

    #!/usr/bin/env zxpy
    
    import toml
    
    def main():
        toml_string = """
    [test]
    x = "something"
    """
        parsed_toml = toml.loads(toml_string)
        print(parsed_toml)
    
    
    if __name__ == '__main__':
        main()
    
    

    I get

    Traceback (most recent call last):
      File "/home/manfred/.local/bin/zxpy", line 8, in <module>
        sys.exit(cli())
      File "/home/manfred/.local/lib/python3.8/site-packages/zx.py", line 56, in cli
        run_zxpy(filename, module)
      File "/home/manfred/.local/lib/python3.8/site-packages/zx.py", line 121, in run_zxpy
        exec(code, None, {'__name__': '__main__'})
      File "./x.py", line 15, in <module>
        main()
      File "./x.py", line 10, in main
        parsed_toml = toml.loads(toml_string)
    NameError: name 'toml' is not defined
    
    opened by manfredlotz 14
  • Parameter for script

    Parameter for script

    In a bash or python script I could have parameters when calling it. This seems to be impossible when using zxpy.

    Example:

    myscript /home

    gives

    Traceback (most recent call last):
      File "/home/manfred/.local/bin/zxpy", line 8, in <module>
        sys.exit(cli())
      File "/home/manfred/.local/lib/python3.8/site-packages/zx.py", line 54, in cli
        with open(filename) as file:
    IsADirectoryError: [Errno 21] Is a directory: '/home'
    

    Is there a way I just overlooked?

    opened by manfredlotz 9
  • Documenting what syntax works and what doesn't

    Documenting what syntax works and what doesn't

    In the zxpy repl I tried various ways of issuing a cmd and some did not work.

    zxpy shell
    Python 3.8.10 (default, Jun  2 2021, 10:49:15) 
    [GCC 9.4.0]
    
    >>> ~'uname'
    Linux
    >>> cmd = 'uname'
    >>> ~f'{cmd}'
    Linux
    >>> ~cmd
    Traceback (most recent call last):
      File "/home/manfred/.local/lib/python3.8/site-packages/zx.py", line 295, in install
        exec(code_obj)
      File "<input>", line 1, in <module>
    TypeError: bad operand type for unary ~: 'str'
    >>> cmd = 'uname -a'
    >>> ~f'{cmd}'
    /bin/sh: 1: uname -a: not found
    >>> ~cmd
    Traceback (most recent call last):
      File "/home/manfred/.local/lib/python3.8/site-packages/zx.py", line 295, in install
        exec(code_obj)
      File "<input>", line 1, in <module>
    TypeError: bad operand type for unary ~: 'str'
    
    opened by manfredlotz 8
  • How to deal with return codes

    How to deal with return codes

    Perhaps I miss something easy.

    But I could not see how I would deal with return codes > 0, i.e a command I issue returns an error. Example: ~'cat /etc/shadow

    opened by manfredlotz 7
  • NameError: name 'run_shell' is not defined

    NameError: name 'run_shell' is not defined

    Sorry for being a pain in the neck.

    But, now using 1.4.2 it seems something else is broken

    Running examples/script.py I get

    Traceback (most recent call last):
      File "/home/manfred/.local/bin/zxpy", line 8, in <module>
        sys.exit(cli())
      File "/home/manfred/.local/lib/python3.8/site-packages/zx.py", line 56, in cli
        run_zxpy(filename, module)
      File "/home/manfred/.local/lib/python3.8/site-packages/zx.py", line 121, in run_zxpy
        exec(code, {'__name__': '__main__'})
      File "./y.py", line 2, in <module>
        ~'echo Hello world!'
    NameError: name 'run_shell' is not defined
    
    opened by manfredlotz 6
  • ~ commands not working in a for loop

    ~ commands not working in a for loop

    The following example

    #!/usr/bin/env zxpy
    
    def main():
        ~'/bin/echo "This works fine"'
        for u in [1, 2]:
            print(u)
            ~'/bin/echo "Not working"'
    
    if __name__ == '__main__':
        main()
    

    gives as output only:

    This works fine
    1
    2
    
    opened by manfredlotz 5
  • No live stdout like in zx

    No live stdout like in zx

    zx displays live output from each command for example:

    #!/usr/bin/env zx
    await $`sudo dnf update`
    
    zx test.mjs
    $ sudo dnf update
    keybase                                          28 kB/s | 3.3 kB     00:00    
    Dependencies resolved.
    ================================================================================
     Package                    Arch     Version                   Repository  Size
    ================================================================================
    Installing:
     kernel                     x86_64   5.11.18-200.fc33          updates    152 k
     kernel-core                x86_64   5.11.18-200.fc33          updates     34 M
     kernel-devel               x86_64   5.11.18-200.fc33          updates     14 M
     kernel-modules             x86_64   5.11.18-200.fc33          updates     31 M
     kernel-modules-extra       x86_64   5.11.18-200.fc33          updates    2.1 M
    Upgrading:
     bind-libs                  x86_64   32:9.11.31-1.fc33         updates     90 k
     bind-libs-lite             x86_64   32:9.11.31-1.fc33         updates    1.1 M
     bind-license               noarch   32:9.11.31-1.fc33         updates     17 k
     bind-utils                 x86_64   32:9.11.31-1.fc33         updates    238 k
     containers-common          noarch   4:1-16.fc33               updates     59 k
     copy-jdk-configs           noarch   4.0-0.fc33                updates     27 k
     cups                       x86_64   1:2.3.3op2-5.fc33         updates    1.3 M
     cups-client                x86_64   1:2.3.3op2-5.fc33         updates     72 k
     cups-filesystem            noarch   1:2.3.3op2-5.fc33         updates     14 k
     cups-ipptool               x86_64   1:2.3.3op2-5.fc33         updates    3.9 M
     cups-libs                  x86_64   1:2.3.3op2-5.fc33         updates    275 k
     ethtool                    x86_64   2:5.12-1.fc33             updates    216 k
     fmt                        x86_64   7.0.3-2.fc33              updates     88 k
     git                        x86_64   2.31.1-3.fc33             updates    122 k
     git-core                   x86_64   2.31.1-3.fc33             updates    3.6 M
     git-core-doc               noarch   2.31.1-3.fc33             updates    2.3 M
     git-credential-libsecret   x86_64   2.31.1-3.fc33             updates     21 k
     git-gui                    noarch   2.31.1-3.fc33             updates    250 k
     gitk                       noarch   2.31.1-3.fc33             updates    164 k
     gnome-online-accounts      x86_64   3.38.2-1.fc33             updates    479 k
     hwdata                     noarch   0.347-1.fc33              updates    1.5 M
     ibus-typing-booster        noarch   2.11.4-1.fc33             updates    902 k
     libnfsidmap                x86_64   1:2.5.3-2.fc33            updates     61 k
     libopenmpt                 x86_64   0.4.20-1.fc33             updates    553 k
     libxcrypt                  x86_64   4.4.20-2.fc33             updates    119 k
     libxcrypt-compat           x86_64   4.4.20-2.fc33             updates     91 k
     libxcrypt-devel            x86_64   4.4.20-2.fc33             updates     29 k
     nfs-utils                  x86_64   1:2.5.3-2.fc33            updates    419 k
     perl-Git                   noarch   2.31.1-3.fc33             updates     44 k
     podman                     x86_64   2:3.2.0-0.1.rc1.fc33      updates     12 M
     podman-plugins             x86_64   2:3.2.0-0.1.rc1.fc33      updates    1.3 M
     python3-babel              noarch   2.8.1-2.fc33              updates    5.7 M
     selinux-policy             noarch   3.14.6-37.fc33            updates     68 k
     selinux-policy-targeted    noarch   3.14.6-37.fc33            updates    8.0 M
     vim-filesystem             noarch   2:8.2.2825-1.fc33         updates     23 k
     vim-minimal                x86_64   2:8.2.2825-1.fc33         updates    695 k
     xdg-desktop-portal         x86_64   1.8.1-2.fc33              updates    354 k
     xdg-desktop-portal-devel   x86_64   1.8.1-2.fc33              updates    8.9 k
     xdg-utils                  noarch   1.1.3-9.fc33              updates     72 k
     zchunk-libs                x86_64   1.1.11-1.fc33             updates     46 k
    Removing:
     kernel                     x86_64   5.11.15-200.fc33          @updates     0  
     kernel-core                x86_64   5.11.15-200.fc33          @updates    74 M
     kernel-devel               x86_64   5.11.15-200.fc33          @updates    56 M
     kernel-modules             x86_64   5.11.15-200.fc33          @updates    30 M
     kernel-modules-extra       x86_64   5.11.15-200.fc33          @updates   1.9 M
    
    Transaction Summary
    ================================================================================
    Install   5 Packages
    Upgrade  40 Packages
    Remove    5 Packages
    Skip      1 Package
    
    Total download size: 127 M
    

    This is not working with zxpy since it's using simple subprocess to execute command and then returns output and only if there is no interactive prompt at the command.

    For example

    #! /usr/bin/env zxpy
    ~'sudo dnf update'
    

    Will just sit there forever without any output.

    opened by JayDoubleu 4
  • Different sys.argv behavour

    Different sys.argv behavour

    Problem description

    When I run a zxpy script, the behaviour of sys.argv is not the same as regular python.

    Example:

    Setup

    I ran poetry init in an empy directory, and used poetry add zxpy to install zxpy (version 1.2.4).

    python3

    I have the following script (test_python.py):

    #!/usr/bin/env python3
    import sys
    print(sys.argv)
    

    If I run the script as poetry run python3 ./test_python.py, the output is:

    ['./test_python.py']
    

    If I then run chmod +x ./test_python.py && poetry run ./test_python.py, the output is:

    ['./test_python.py']
    

    zxpy

    I have the following script (test_zxpy.py):

    #!/usr/bin/env zxpy
    import sys
    print(sys.argv)
    

    If I run the script as poetry run zxpy ./test_zxpy.py, the output is:

    ['/home/techhazard/.cache/pypoetry/virtualenvs/aoeuaoe-BGFxEDCR-py3.9/bin/zxpy', 'test_zxpy.py']
    

    If I then run chmod +x ./test_zxpy.py && poetry run ./test_zxpy.py, the output is:

    ['/home/techhazard/.cache/pypoetry/virtualenvs/aoeuaoe-BGFxEDCR-py3.9/bin/zxpy', 'test_zxpy.py']
    

    Expected behaviour:

    When zxpy is run, the sys.argv values should not include the zxpy executable.

    opened by techhazard 3
  • unary operator syntax doesn't work with print()

    unary operator syntax doesn't work with print()

    Code:

    #! /usr/bin/env zxpy
    
    var = "test string"
    
    print(~f"echo {var}")
    

    Output:

    $ ./simplescript.py
    Traceback (most recent call last):
      File "/home/ubuntu/zxpy-test/venv/bin/zxpy", line 8, in <module>
        sys.exit(cli())
      File "/home/ubuntu/zxpy-test/venv/lib/python3.8/site-packages/zx.py", line 55, in cli
        run_zxpy(filename, module)
      File "/home/ubuntu/zxpy-test/venv/lib/python3.8/site-packages/zx.py", line 85, in run_zxpy
        exec(compile(module, filename, mode='exec'))
      File "./simplescript.py", line 5, in <module>
        print(~f"echo {var}")
    TypeError: bad operand type for unary ~: 'str'
    

    zxpy version: 1.2.3

    opened by Jackenmen 3
  • bad operand type for unary ~: 'str'

    bad operand type for unary ~: 'str'

    Hi

    This is my code for find some zip in dir but it cause the exception below l = (~f'find -maxdepth 1 -name "*{x}*.zip" -type f').splitlines()

    Traceback (most recent call last):
     File "/usr/local/bin/zxpy", line 8, in <module>
       sys.exit(cli())
     File "/usr/local/lib/python3.8/dist-packages/zx.py", line 55, in cli
       run_zxpy(filename, module)
     File "/usr/local/lib/python3.8/dist-packages/zx.py", line 85, in run_zxpy
       exec(compile(module, filename, mode='exec'))
     File "./test.py", line 21, in <module>
       l = (~f'find -maxdepth 1 -name "*{x}*.zip" -type f').splitlines()
    TypeError: bad operand type for unary ~: 'str'
    

    when i split this command into two line, and all is fine

    l = ~f'find -maxdepth 1 -name "*{x}*.zip" -type f'
    l = l.splitlines()
    

    By the way, data = (~'ls some_dir/').splitlines() this command is ok. So maybe it only occurs on format string?

    Thanks a lot


    Ubuntu 20.04.2 LTS AArch64 Python 3.8.5 zxpy 1.2.1

    opened by tjjh89017 3
  • Code cleanup

    Code cleanup

    • [ ] #35
    • [x] Fix unused import in tests
    • [x] Fix Python 3.6/3.7 support (use pytest-typing-imports)
    • [x] Remove __main__.py
    • [x] Sort gitignore
    • [x] Setup black
    • [x] Simple refactors (like sys.argv)
    opened by tusharsadhwani 0
Releases(1.6.2)
Owner
Tushar Sadhwani
Contact: [email protected] • telegram: @tusharsadhwani • i
Tushar Sadhwani
JLC2KICAD_lib is a python script that generate a component library for KiCad from the JLCPCB/easyEDA library.

JLC2KiCad_lib is a python script that generate a component library (schematic, footprint and 3D model) for KiCad from the JLCPCB/easyEDA library. This script requires Python 3.6 or higher.

Nicolas Toussaint 73 Dec 26, 2022
Decentralized intelligent voting application.

DiVA Decentralized intelligent voting application. Hack the North 2021. Inspiration Following the previous US election, many voters were fearful that

Ali Shariatmadari 4 Jun 05, 2022
CalHacks 8 Repo: Megha Jain, Gaurav Bhatnagar, Howard Meng, Vibha Tantry

CalHacks8 CalHacks 8 Repo: Megha Jain, Gaurav Bhatnagar, Howard Meng, Vibha Tantry Setup FE Install React Native via Expo, run App.js. Backend Create

0 Aug 20, 2022
Herramienta para poder automatizar reuniones en Zoom.

Crear Reunión Zoom con Python Herramienta para poder automatizar reuniones en Zoom. Librerías Requeridas Nombre Comando PyAutoGui pip install pyautogu

JkDev 3 Nov 12, 2022
JupyterLite as a Datasette plugin

datasette-jupyterlite JupyterLite as a Datasette plugin Installation Install this plugin in the same environment as Datasette. $ datasette install dat

Simon Willison 11 Sep 19, 2022
Cross-platform MachO/ObjC Static binary analysis tool & library. class-dump + otool + lipo + more

ktool Static Mach-O binary metadata analysis tool / information dumper pip3 install k2l Development is currently taking place on the @python3.10 branc

Kritanta 301 Dec 28, 2022
Project aims to map out common user behavior on the computer

User-Behavior-Mapping-Tool Project aims to map out common user behavior on the computer. Most of the code is based on the research by kacos2000 found

trustedsec 136 Dec 23, 2022
Data Applications Project

DBMS project- Hotel Franchise Data and application project By TEAM Kurukunda Bhargavi Pamulapati Pallavi Greeshma Amaraneni What is this project about

Greeshma 1 Nov 28, 2021
Awesome Casino is simple offline casino made on python.

Awesome-Casino Awesome Casino is simple offline casino made on python. I found bug, what can i do? If you find any bug or want to suggest any idea, al

Herman 1 Feb 04, 2022
a wordle-solver written in python

Wordle Solver Overview This is yet another wordle solver. It is built with the word list of the official wordle website, but it should also work with

Shoubhit Dash 10 Sep 24, 2022
A plugin for poetry that allows you to execute scripts defined in your pyproject.toml, just like you can in npm or pipenv

poetry-exec-plugin A plugin for poetry that allows you to execute scripts defined in your pyproject.toml, just like you can in npm or pipenv Installat

38 Jan 06, 2023
GDIT: Geometry Dash Info Tool

GDIT: Geometry Dash Info Tool This is the first large script that allows you to quickly get information from the Geometry Dash server

dezz0xY 2 Jan 09, 2022
A simple weather app.

keather A simple weather app. This is currently not finished. Dependencies: yay -S python-beautifulsoup4 tk

1 Jan 09, 2022
Simple application that does transformation with HPF and LPFs.

Simple application that applies Butterworth, Gaussian & Ideal kernels on HPF and LPFs -aka Frequency Domain Filtering- Upload image from sidebar, set

Merve Noyan 3 Jul 06, 2022
A continuation Of Project Glow By @glowstik-yt

Project Glow Greetings, I see you have stumbled upon project glow. Project glow is an open source bot worked on by many people to create a good and sa

1 Nov 17, 2021
Python package for handling and analyzing PSRFITS files

PyPulse A pure-Python package for handling and analyzing PSRFITS files. Read the documentation here. This is an alternate code base from PSRCHIVE. Req

Michael Lam 15 Nov 30, 2022
Wrappers around the most common maya.cmds and maya.api use cases

Maya FunctionSet (maya_fn) A package that decompose core maya.cmds and maya.api features to a set of simple functions. Tests The recommended approach

Ryan Porter 9 Mar 12, 2022
March-madness - March Madness results 1985-2021

march-madness Results for all 2,268 NCAA Division I Men's Basketball Tournament games since the modern format was introduced in 1985. Includes years,

Darik Harter 2 Feb 26, 2022
Library for mocking AsyncIOMotorClient built on top of mongomock.

mongomock-motor Best effort mock for AsyncIOMotorClient (Database, Collection, e.t.c) built on top of mongomock library. Example / Showcase from mongo

Michael Kryukov 43 Jan 04, 2023
Hashcrack: Hash Bruteforse tool using python

HashCrack Hash Bruteforse tool Usage hashcrack.py -n 6 -c lower -l 5 -a md5 -t 3

Lev 1 May 04, 2022