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
原神抽卡记录导出

原神抽卡记录导出 抽卡记录分析工具 from @笑沐泽 抽卡记录导出工具js版,含油猴脚本可在浏览器导出 注意:我的是python版,带饼图的是隔壁electron版,功能类似 Wik

834 Jan 04, 2023
laTEX is awesome but we are lazy -> groff with markdown syntax and inline code execution

pyGroff A wrapper for groff using python to have a nicer syntax for groff documents DOCUMENTATION Very similar to markdown. So if you know what that i

Subhaditya Mukherjee 27 Jul 23, 2022
Completed task 1 and task 2 at LetsGrowMore as a data science intern.

LetsGrowMore-Internship Completed task 1 and task 2 at LetsGrowMore as a data science intern. Task 1- Task 2- Creating a Decision Tree classifier and

Sanjyot Panure 1 Jan 16, 2022
A performant state estimator for power system

A state estimator for power system. Turbocharged with sparse matrix support, JIT, SIMD and improved ordering.

9 Dec 12, 2022
Whatsapp Messenger master

Whatsapp Messenger master

Swarup Kharul 5 Nov 21, 2021
Mengzhan (John) code for Closed Loop Control system of Sharp Wave Ripples in Hippocampus CA3 region

ClosedLoopControl_Yu Mengzhan (John) code for Closed Loop Control system of Sharp Wave Ripples in Hippocampus CA3 region Creating Python Virtual Envir

Mengzhan (John) Liufu 1 Jan 22, 2022
Oppia is an online learning tool that enables anyone to easily create and share interactive activities

Oppia is an online learning tool that enables anyone to easily create and share interactive activities (called 'explorations'). These activities simulate a one-on-one conversation with a tutor, makin

Oppia 4.7k Dec 29, 2022
Howell County, Missouri, COVID-19 data and (unofficial) estimates

COVID-19 in Howell County, Missouri This repository contains the daily data files used to generate my COVID-19 dashboard for Howell County, Missouri,

Jonathan Thornton 0 Jun 18, 2022
Explores the python bytecode, provides some tools to access it for fun and profit.

Pyasmtools - looking at the python bytecode for fun and profit. The pyasmtools library is made up of two parts A python bytecode disassembler . See Py

Michael Moser 299 Jan 04, 2023
RFDesign - Protein hallucination and inpainting with RoseTTAFold

RFDesign: Protein hallucination and inpainting with RoseTTAFold Jue Wang (juewan

139 Jan 06, 2023
🦋 hundun is a python library for the exploration of chaos.

hundun hundun is a python library for the exploration of chaos. Please note that this library is in beta phase. Example Import the package's equation

kosh 7 Nov 07, 2022
GNU/Linux'u yeni kurulumu bitirmiş olarak açtığınızda sizi karşılayacak bir uygulama.

Hoş Geldiniz GNU/Linux'u yeni kurulumu bitirmiş olarak açtığınızda sizi karşılayacak bir uygulama.

Alperen İsa 96 Oct 30, 2022
Find out where all films you want to watch are streaming

Just Watch Letterboxd Find out where all films you want to watch are streaming Ever wonder what films you want to watch are already on the streaming p

Jordan Oslislo 2 Feb 04, 2022
python DroneCAN code generation, interface and utilities

UAVCAN v0 stack in Python Python implementation of the UAVCAN v0 protocol stack. UAVCAN is a lightweight protocol designed for reliable communication

DroneCAN 11 Dec 12, 2022
Placeholders is a single-unit storage solution for your Frontend.

Placeholder Placeholders is a single-unit file storage solution for your Frontend. Why Placeholder? Generally, when a website/service requests for fil

Tanmoy Sen Gupta 1 Nov 09, 2021
Генератор отчетов на Python с использованием библиотеки docx для работы с word-файлами и запросов к сервису

Генератор отчетов на Python с использованием библиотеки docx для работы с word-файлами и запросов к сервису

Semyon Esaev 2 Jun 24, 2022
Manage Procfile-based applications

Foreman Manage Procfile-based applications Installation $ gem install foreman Ruby users should take care not to install foreman in their project's G

David Dollar 5.8k Jan 03, 2023
RecurrentArchitectures - See the accompanying blog post

Why this? What is the goal? The goal of this repository is to write all the recurrent architectures from scratch in tensorflow for learning purposes.

Debajyoti Datta 9 Feb 06, 2022
Coffeematcher is a python library to randomly match participants for coffee meetings.

coffeematcher coffeematcher is a python library to randomly match participants for coffee meetings. Installation Clone the repository: git clone https

Thomas Wesselink 3 May 06, 2022
Custom Python code for calculating the Probability of Profit (POP) for options trading strategies using Monte Carlo Simulations.

Custom Python code for calculating the Probability of Profit (POP) for options trading strategies using Monte Carlo Simulations.

35 Nov 22, 2022