Remix.run Logo
theamk 15 days ago

It does not have to be all-at-once.

Your bash program takes 10 input args and requires 15 env variables to be set? Don't reach out for that bash ini parser, create a python script which parses configs and invokes the original bash script. Next time you change feature, move it to python. Eventually you'll be mostly python.

Something that helps greatly with that is that you can embed bash in python. So initial conversion can be as simple as file rename + 3 lines of code.

    import subprocess
    subprocess.run(["bash", "-c", """
    echo This is bash
    if hostname | grep box; then
       echo running on a box
    fi
    """], check=True)
this lets you start by trivial .sh -> .py conversion, and then you can change one block at a time. Variables are tricky so do them first; or in the worst case, "declare -p" + "eval" + temporary files will save you.
quotemstr 15 days ago | parent [-]

It's not that simple. How are you passing argv to the bash script? What about how `subprocess.run` changes the error output to include Python exception spam? Plus, in most editors, you lose all the bash syntax highlighting in the bash program chunk. I haven't seen a lot of hybrid programs like this work well.

pwdisswordfishz 14 days ago | parent | next [-]

That's why it's just the first stage of an incremental rewrite.

argv is easy to pass, though.

theamk 14 days ago | parent | prev [-]

It is that simple. You can pass arguments to `bash -c` with no problems(just don't forget a dummy $0). If you don't want exception spam, replace `check=True` with explicit exit. Here you go, it's 5 lines now.

    #!/usr/bin/env python3
    import sys
    import subprocess
    result = subprocess.run(["bash", "-c", """
    echo I am in bash, I have $# args, arg1 is "$1"
    set -x
    "$@"
    """, "dummy.sh"] + sys.argv[1:])
    sys.exit(result.returncode)
python's shell tooling is really good (if a bit verbose). And another cool thing is "shlex.quote", so you can write something like:

    subprocess.run(["bash", "-c", f"rsync {shlex.quote(filename)} somehost: | some-script"])
and have "filename" safely inserted into script fragment.. No command escapes, and newlines, quotes, backticks etc... are all handled properly.

The bash syntax highlighting will disappear, correct, and that is a downside. But hey, you are supposed to be rewriting this in python, so it's just more motivation :)