Reputation: 137
I'm writing a python program which passes arguments to a shell script.
Here's my python code:
import subprocess
Process=subprocess.Popen('./copyImage.sh %s' %s str(myPic.jpg))
And my "copyImage.sh":
#!/bin/sh
cp /home/pi/project/$1 /home/pi/project/newImage.jpg
I can run the script on terminal without problems. But when executing the python code, the terminal returned "NameError: name 'myPic' is not defined"
.
If I change the syntax to
Process=subprocess.Popen('./copyImage.sh %s' %s "myPic.jpg")
Then the terminal returned "OSError: [Errno 2] No such file or directory"
.
I've followed this: Python: executing shell script with arguments(variable), but argument is not read in shell script but it didn't help.
Upvotes: 3
Views: 8639
Reputation: 2647
Using os.system call is the way to go as:
Example:
os.system('myshellscript1 ' + arg1 + ' ' + arg2)
Upvotes: 3
Reputation: 14490
While you got 2 answers that show how to use subprocess
with an iterable for the arguments and I would recommend going one of those ways, for completeness you can use a string containing the full command if you specify shell=True
, but then you're responsible for all the quoting and everything like that for arguments.
Process=subprocess.Popen('./copyImage.sh %s' % shlex.quote("myPic.jpg"), shell=True)
Note that in addition to adding shell=True
I pass the argument through shlex.quote
to let it handle escaping any special characters to make it a bit safer if the filename came from a user input, otherwise it could include a ;
and another command to run, for example. Input like myPic.jpg; rm -rf ~
would otherwise cause bad things to happen when executed.
If you don't specify shell=True
the subrpocess module will actually be looking for an executable named copyImage.sh myPic.jpg
with the space and both words as the name of the executable to run.
Two further notes, for python 2 instead of shlex.quote
use pipes.quote
. Also, the shell script above does not quote its arguments, so will not work with names with spaces or other special characters. It should be modified to quote its variables (which is always a good idea):
#!/bin/sh
cp /home/pi/project/"$1" /home/pi/project/newImage.jpg
With a slightly different script:
#!/bin/bash
printf 'Arg 1 is: %s\n' "$1"
we can see this work as follows:
subprocess.check_call("./demoScript.sh %s" % shlex.quote("This has ; bad stuff"), shell=True)
which produces the following output on stdout
Arg 1 is: This has ; bad stuff
Upvotes: 0
Reputation: 362507
The subprocess
module is expecting a list of arguments, not a space-separated string. The way you tried caused python to look for a program called "copyImage.sh myPic.jpg"
and call it with no arguments, whereas you wanted to look for a program called copyImage.sh
and call it with one argument.
subprocess.check_call(['copyImage.sh', 'myPic.jpg'])
I also want to mention, since your script simply calls copy in a shell, you should probably cut out the middleman and just use python's shutil.copy
directly. It's a more appropriate tool than running a subprocess for this task.
Upvotes: 3
Reputation: 123410
The safe and robust way is:
subprocess.Popen(["./copyImage.sh", "myPic.jpg"])
Your first attempt failed because string literals need quotes in Python. The second one failed because Popen doesn't run a shell by default (the question you link sets Shell=true
to do this, but it's fragile and bad).
Upvotes: 1