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
Daily knowledge pills to get better in Python.

Python daily pills Daily knowledge pills to get better Python code. Why Does your Python code suffers of any of this symptoms? Incorrect Indentation I

Jeferson Vaz dos Santos 35 Sep 19, 2022
Hartree-Fock Workshop for the Han-sur-Lesse Winterschool of 2021

Hartree-Fock course for the Han-sur-Lesse Winterschool of 2021 Requirements For going through these exercises, please install the Anaconda suite. Next

Ivo Filot 2 Nov 16, 2022
A collection of resources on neural rendering.

awesome neural rendering A collection of resources on neural rendering. Contributing If you think I have missed out on something (or) have any suggest

1.8k Dec 30, 2022
Workshop OOP - Workshop OOP - Discover object-oriented programming

About: This is an open-source bot, the code is open for anyone to see, fork and

Francis Clairicia-Rose-Claire-Josรฉphine 5 May 02, 2022
An a simple sistem code in python

AMS OS An a simple code in python โ•ยฟWhat is AMS OS? AMS OS is an a simple sistem code writed in python. This code helps you with the cotidian task, yo

1 Nov 10, 2021
A notebook explaining the principle of adversarial attacks and their defences

TL;DR: A notebook explaining the principle of adversarial attacks and their defences Abstract: Deep neural networks models have been wildly successful

1 Jan 22, 2022
Rock ๐Ÿ’Ž Paper ๐Ÿ“ Scissors โœ‚๏ธ Lizard ๐ŸฆŽ Spock ๐Ÿ––

Rock ๐Ÿ’Ž Paper ๐Ÿ“ Scissors โœ‚๏ธ Lizard ๐ŸฆŽ Spock ๐Ÿ–– If youโ€™ve seen The Big Bang Theory, youโ€™ve heard of a game called โ€œRock, Paper, Scissors, Lizard, Spoc

AmirHossein Mohammadi 16 Jun 19, 2022
A simple method to create strong password.

A simple method to create strong password.

1 Jan 23, 2022
An Insurance firm providing tour insurance is facing higher claim frequency

An Insurance firm providing tour insurance is facing higher claim frequency. Data is collected from the past few years. Made a model which predicts the claim status using CART, RF & ANN and compare t

1 Jan 27, 2022
A simple and easy to use Python's PIP configuration manager, similar to the Arch Linux's Java manager.

PIPCONF - The PIP configuration manager If you need to manage multiple configurations containing indexes and trusted hosts for PIP, this project was m

Joรฃo Paulo Carvalho 11 Nov 30, 2022
A simplified python interface to COPASI.

BasiCO This project hosts a simplified python interface to COPASI. While all functionality from COPASI is exposed via automatically generated SWIG wra

COPASI 8 Dec 21, 2022
A very terrible python-based programming language that uses folders instead of text files

PYFolders by Lewis L. Foster PYFolders is a very terrible python-based programming language that uses folders instead of regular text files. In this r

Lewis L. Foster 5 Jan 08, 2022
An open-source systems and controls toolbox for Python3

harold A control systems package for Python=3.6. Introduction This package is written with the ambition of providing a full-fledged control systems s

Ilhan Polat 157 Dec 05, 2022
Add all JuliaLang unicode abbreviations to AutoKey.

Autokey Unicode characters Usage This script adds all the unicode character abbreviations supported by Julia to autokey. However, instead of [TAB], th

Randolf Scholz 49 Dec 02, 2022
A web-based analysis toolkit for the System Usability Scale providing calculation, plotting, interpretation and contextualization utility

System Usability Scale Analysis Toolkit The System Usability Scale (SUS) Analysis Toolkit is a web-based python application that provides a compilatio

Jonas Blattgerste 3 Oct 27, 2022
SuperMario - Python programming class ending assignment SuperMario, using pygame

SuperMario - Python programming class ending assignment SuperMario, using pygame

mars 2 Jan 04, 2022
A frontend to ease the use of pulseaudio's routing capabilities, mimicking voicemeeter's workflow

Pulsemeeter A frontend to ease the use of pulseaudio's routing capabilities, mimicking voicemeeter's workflow Features Create virtual inputs and outpu

Gabriel Carneiro 164 Jan 04, 2023
The Blinker Herald includes helpers to easily emit signals using the excellent blinker library.

Blinker Herald The Blinker Herald includes helpers to easily emit signals using the excelent blinker library. Decorate a function or method with @blin

SatelliteQE 7 Nov 03, 2022
A Way to Use Python, Easier.

PyTools A Way to Use Python, Easier. How to Install Just copy this code, then make a new file in your project directory called PyTools.py, then paste

Kamran 2 Aug 15, 2022
Here, I have discuss the three methods of list reversion. The three methods are built-in method, slicing method and position changing method.

Three-different-method-for-list-reversion Here, I have discuss the three methods of list reversion. The three methods are built-in method, slicing met

Sachin Vinayak Dabhade 4 Sep 24, 2021