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
Your Google Recon is Now Automated

GRecon : GRecon (Greei-Conn) is a simple python tool that automates the process of Google Based Recon AKA Google Dorking The current Version 1.0 Run 7

adnane-tebbaa 189 Dec 21, 2022
A repo to record how I prepare my Interview, and really hope it can help you as well. Really appreciate Kieran's help in the pattern's part.

Project Overview The purpose of this repo is to help others to find solutions and explaintion I will commit a solution and explanation to every proble

Vincent Zhenhao ZHAO 1 Nov 29, 2021
Advanced Keylogger in Python

Advanced Keylogger in Python Important Disclaimer: The author will not be held r

Suvanth Erranki 1 Feb 07, 2022
A web interface for a soft serve Git server.

Soft Serve monitor Soft Sevre is a very nice git server. It offers a really nice TUI to browse the repositories on the server. Unfortunately, it does

Maxime Bouillot 5 Apr 26, 2022
An AddOn storing wireguard configuration

Wireguard Database Connector Overview Development Status: 0.1.7 (alpha) First of all, I'd like to thank Jared McKnight for wireguard who inspired me t

Markus Neubauer 3 Dec 30, 2021
A simply dashboard to view commodities position data based on CFTC reports

commodities-dashboard A simply dashboard to view commodities position data based on CFTC reports This is a python project using Dash and plotly to con

71 Dec 19, 2022
Python version of RocketLeague-Dropshot-Calculated-shot

Python version of RocketLeague-Dropshot-Calculated-shot. This is just to demo around and a tool I used to develop the actual plugin.

JareBear 1 Jan 14, 2022
Checks for Vaccine Availability at your district and notifies you using E-mail, subscribe to our website.

Vaccine Availability Notifier Project Description Checks for Vaccine Availability at your district and notifies you using E-mail every 10 mins. Kindly

Farhan Hai Khan 19 Jun 03, 2021
Code for Crowd counting via unsupervised cross-domain feature adaptation.

CDFA-pytorch Code for Unsupervised crowd counting via cross-domain feature adaptation. Pre-trained models Google Drive Baidu Cloud : t4qc Environment

Guanchen Ding 6 Dec 11, 2022
Automatically give thanks to Pypi packages you use in your project!

Automatically give thanks to Pypi packages you use in your project!

Ward 25 Dec 20, 2021
Backups made easy, automated, monitored and SECURED with an audited encryption

Backup Controller Backups made easy, automated, monitored and SECURED with an audited encryption. Schedules backup tasks executed by Backup Maker, upl

RiotKit 1 Jan 30, 2022
Think DSP: Digital Signal Processing in Python, by Allen B. Downey.

ThinkDSP LaTeX source and Python code for Think DSP: Digital Signal Processing in Python, by Allen B. Downey. The premise of this book (and the other

Allen Downey 3.2k Jan 08, 2023
Expense Tracker is a very good tool to keep track of your expenseditures and the total money you saved.

Expense Tracker is a very good tool to keep track of your expenseditures and the total money you saved.

Shreejan Dolai 9 Dec 31, 2022
Addon for Blender 2.8+ that automatically creates NLA tracks for all animations. Useful for GLTF export.

PushDownAll An addon for Blender 2.8+ that runs Push Down on all animations, creating NLA tracks for each. This is useful if you have an object with m

Cory Petkovsek 16 Oct 06, 2022
A type based dependency injection framework for Python 3.9+

Alluka A type based dependency injection framework for Python 3.9+. Installation You can install Alluka from PyPI using the following command in any P

Lucina 16 Dec 15, 2022
HashDB Binary Ninja Plugin

HashDB Plugin (v0.1) Author: Vector 35 Inc Plugin for interacting with the OALABS HashDB service. Description: Plugin that can be used to lookup hashe

Jordan 3 Jul 30, 2022
Hera is a Python framework for constructing and submitting Argo Workflows.

Hera is an Argo Workflows Python SDK. Hera aims to make workflow construction and submission easy and accessible to everyone! Hera abstracts away workflow setup details while still maintaining a cons

argoproj-labs 241 Jan 02, 2023
to learn how to do pull request and do contribution to other's repo

Hacktoberfest-2021 - open-source-contribution An Open Source repository to Teach people How to contribute to open sources. 💥 🔥 JOIN PVX PROGRAMMING

Shubham Rawat 82 Dec 26, 2022
Choice Coin 633 Dec 23, 2022
Projects using the Tkinter module in Python!

Tkinter projects This repository includes some Tkinter projects made by me. All of these are simple to understand. I create apps with good functionali

Amey 0 Sep 24, 2021