Single quotes already work exactly as you would wish. Probably you are doing something wrong with the string inside of your Python script.
In some more detail, the arguments when Bash is done parsing this command are (one per line)
python
script.py
-o
ls -la /tmp > /tmp/test
(The quotes were useful while Bash was parsing this command line, but they are gone now.)
You should easily be able to verify writhin Python that sys.argv[0]
is script.py
, sys.argv[1]
is -o
, and sys.argv[2]
is the single-quoted string, sans quotes; all of these are strings (the shell really doesn't have any other primitive data type).
Backslash escaping all shell metacharacters individually would work as well here;
python script.py -o ls\ -la\ /tmp\ \>\ test
or double quoting the string; albeit in the shell, double quotes have slightly different semantics (variables will be interpolated and backticks evaluated, and a backslash can be used to escape backslashes, double quotes, dollar signs, and backticks, unlike in single quotes, where every character is preserved verbatim, and there is no way to escape a character.)
You don't reveal how exactly your code doesn't work, but I'd speculate that your problem is actually the opposite; you end up running something like ls '>'
and get an error message that this file does not exist.
For the record, >
is a redirection operator, not a command. The shell parses this into
ls
-la
/tmp
with standard oqtput redirected to the file /tmp/test
. In terms of Python code,
with open('/tmp/test', 'w') as redirect:
subprocess.run(['ls', '-la', '/tmp'], stdout=redirect)
Of course, if you want to support arbitrary shell features, you need a shell. The simplest fix here is probably
subprocess.run(sys.argv[2], shell=True)
See Actual meaning of shell=True
in subprocess
but here, I don't see any easy way to avoid the shell, short of by writing your own reimplementation.