Python Bash – Encoding a Bash Script for Use in Python

bashpython

I am writing some code in Python which checks for the validity of a license key after polling a server. If it is valid then the Python program in turn runs some bash scripts.

How to create a combined binary of the python script (to check the license) and the bash script (which performs the task)?

Here are what I think could be the solutions but I am really not sure:

  1. I use the encode function in python and encode the bash script file once and add combine that file and the license_checker.py file into a single binary. When the binary runs, the python code should decode the file into a temp file, run the script and delete the temp file.
  2. Find a mechanism which wraps/embeds the bash code in the python script. I have heard of decorators but I haven't used them and do not know if they would serve the purpose.

Best Answer

I'd use the first option; embed the bash script in a multi-line string in the Python script, then write that out and use the subprocess module to run the script:

import tempfile
import subprocess

script_one = '''\
echo "Hello world!"
echo "This is a bash script. :-)"
'''

def run_script(script):
    with tempfile.NamedTemporaryFile() as scriptfile:
        scriptfile.write(script)
        scriptfile.flush()
        subprocess.call(['/bin/bash', scriptfile.name])

run_script(script_one)

The run_script() function takes a bash script source, writes it to a temporary file, executes it, and automatically cleans up the temporary file when done.

Demo:

$ python /tmp/scripttest.py 
Hello world!
This is a bash script. :-)

Python decorators are of no help here; there is no point in looking at those; decorators are simply Python functions that get to wrap other Python functions.

If the script is large, you could try compressing it with zlib, base64 encode it, then embed that into the Python script file; using .decode('base64') and passing the result to the zlib module first gives you a decompressed string again. The ZLIB compression reduces the size but results in binary data, always a little harder to embed without having to deal with proper escaping and line lengths. The Base64 encoding turns the binary data into multi-line ASCII-safe data again, albeit at the price of 25% more bytes:

>>> import zlib
>>> zlib.compress(script_one).encode('base64')
'eJxLTc7IV1DySM3JyVcozy/KSVFU4koFi4VkZBYrAFGiQlJicYZCcXJRZkGJnoKVrqYSFwDnexDe\n'
>>> print """script_one = '''\\\n{}'''""".format(_)
script_one = '''\
eJxLTc7IV1DySM3JyVcozy/KSVFU4koFi4VkZBYrAFGiQlJicYZCcXJRZkGJnoKVrqYSFwDnexDe
'''

Note that for this short script sample, this increased the size, from 54 characters to 77, but for longer scripts you should see a decent size reduction.

Decompression is then as simple as zlib.decompress(script_one.decode('base64'))