Reputation: 9073
I want to run the following command using Python on a Mac OS X computer:
openssl enc -aes-128-cbc -K $(echo -n 'blabla1' | xxd -p) -iv blabla2 -in /tmp/clair -nopad -out /tmp/crypte1 -d
Here is what I have tried:
clef = 'blabla1'
iv = 'blabla2'
arguments = ['openssl','enc', '-aes-128-cbc', '-K $(echo -n \'%s\' | xxd -p)' % clef ,'-iv' ,'%s' % iv,'-in', '/tmp/clair','-nopad','-out','/tmp/crypte1','-d']
execute = Popen(arguments, stdout=PIPE)
out, err = execute.communicate()
The command works fine from a terminal but I get an error from the Python script:
unknown option '-K $(echo -n 'blabla1' | xxd -p)'
I have tried several variants of python shell functions (os.system
for example), but I have a problem in each case.
Upvotes: 2
Views: 1968
Reputation: 1691
subprocess
adds quotes "
to the argument "-K $(echo -n 'blabla1' | xxd -p)"
. You can check by
print(subprocess.list2cmdline(execute.args))
Output:
openssl enc -aes-128-cbc "-K $(echo -n 'blabla1' | xxd -p)" -iv blabla2 -in /tmp/clair -nopad -out /tmp/crypte1 -d
Upvotes: 0
Reputation: 114270
There are a number of things that users often take for granted when using a shell. Some examples are:
${var}
)cmd > out < in
)cmd1 | cmd2
)$(cmd)
)All of these are features that the shell sets up before passing the actual command to the system. Python's subprocess
module does not do any of these things for you automatically, but it does give you the tools to emulate them.
You have correctly redirected your output to a pipe for your Python process to pick up. However, the subshell created by $(echo -n 'blabla1' | xxd -p)
is not something that will get processed the way you want without a shell. There are two simple workarounds.
The quick and dirty solution is to pass the entire command line in as a string to subprocess.Popen
and set shell=True
:
execute = Popen("openssl enc -aes-128-cbc -K $(echo -n 'blabla1' | xxd -p) "
"-iv blabla2 -in /tmp/clair -nopad -out /tmp/crypte1 -d",
shell=True)
This will pass the string to a shell instead of directly to the system, thereby giving you access all the behaviors that you expect from a shell. This approach has major security problems, and is not generally recommended. However, it is very easy, and it will get you started.
Implement the convenience of $(... | ...)
in your code directly by capturing the output of xxd
to a string using subprocess.run
. This is lengthier, but probably more robust and portable in the long run. It is certainly the option I would chose in a production environment:
from subprocess import Popen, run
value = run(['xxd', '-p'], input='blabla1', universal_newlines=True).stdout
execute = Popen(['openssl', 'enc', '-aes-128-cbc', '-K', value,
'-iv', 'blabla2', '-in', '/tmp/clair', '-nopad',
'-out', '/tmp/crypte1', '-d'])
Since you are setting up your own pipes, you don't need to call echo
any more (you never did really, xxd -p <<< 'blabla1'
would have worked fine), and -K
needs to be a separate argument.
Upvotes: 4