#!/usr/bin/python3
import base64
import sys
import subprocess
def shell_safe(cmd):
"""shell_safe(cmd): return a string that can be passed to
set -- <string>
to produce the same array that cmd represents.
internally we utilize 'getopt's ability/knowledge on how to quote
strings to be safe for shell. This implementation could be changed
to be pure python. It is just a matter of correctly escaping
or quoting characters like: ' " ^ & $ ; ( ) ... """
out = subprocess.check_output(
["getopt", "--shell", "sh", "--options", "", "--", "--"] + list(cmd))
# out contains ' -- <data>\n'. drop the ' -- ' and the '\n'
return out[4:-1].decode()
def shell_pack(cmd):
"""shell_pack(cmd): return a string that can shuffled through 'sh'
and will execute cmd.
in python subprocess terms:
check_output(cmd) == check_output(shell_pack(cmd), shell=True)"""
if isinstance(cmd, str):
cmd = [cmd]
else:
cmd = list(cmd)
stuffed = shell_safe(cmd)
# for whatever reason b64encode returns bytes when it is clearly
# representable as a string by nature of being base64 encoded.
b64 = base64.b64encode(stuffed.encode()).decode()
return 'eval set -- "$(echo %s | base64 --decode)" && exec "$@"' % b64
args = sys.argv[1:]
shell_packed = shell_pack(args)
print("input: %s" % args)
print("shellpack: %s" % shell_packed)
print("output: %s" % subprocess.check_output(shell_packed, shell=True))