161

I have a problem with the following code:

callBash.py:

import subprocess
print "start"
subprocess.call("sleep.sh")
print "end"

sleep.sh:

sleep 10

I want the "end" to be printed after 10s. (I know that this is a dumb example, I could simply sleep within python, but this simple sleep.sh file was just as a test)

user1638145
  • 1,979
  • 2
  • 14
  • 14
  • I also tried it with "$!bin/bash; sleep 10; " – user1638145 Dec 06 '12 at 14:29
  • 2
    It is unclear what problem this questien is trying to ask about, but the question should probably remain because it has accrued a historical grab bag of more or less good guesses as answers. Probably also search for your specific error, and/or read related questions like https://stackoverflow.com/questions/4256107/running-bash-commands-in-python – tripleee Jun 02 '20 at 02:08

7 Answers7

121

Making sleep.sh executable and adding shell=True to the parameter list (as suggested in previous answers) works ok. Depending on the search path, you may also need to add ./ or some other appropriate path. (Ie, change "sleep.sh" to "./sleep.sh".)

The shell=True parameter is not needed (under a Posix system like Linux) if the first line of the bash script is a path to a shell; for example, #!/bin/bash.

James Waldby - jwpat7
  • 8,593
  • 2
  • 22
  • 37
  • 1
    Just as additional information `shell=True` is discouraged - [docs](https://docs.python.org/2/library/subprocess.html#frequently-used-arguments) because it makes your program vulnerable to shell injections – user20068036 Feb 21 '23 at 18:28
73

If sleep.sh has the shebang #!/bin/sh and it has appropriate file permissions -- run chmod u+rx sleep.sh to make sure and it is in $PATH then your code should work as is:

import subprocess

rc = subprocess.call("sleep.sh")

If the script is not in the PATH then specify the full path to it e.g., if it is in the current working directory:

from subprocess import call

rc = call("./sleep.sh")

If the script has no shebang then you need to specify shell=True:

rc = call("./sleep.sh", shell=True)

If the script has no executable permissions and you can't change it e.g., by running os.chmod('sleep.sh', 0o755) then you could read the script as a text file and pass the string to subprocess module instead:

with open('sleep.sh', 'rb') as file:
    script = file.read()
rc = call(script, shell=True)
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • Thanks for your work. is there a way to do this without using `import` I want to use your code for homework and we cant use import in the py file – c0d3x27 Aug 03 '22 at 15:49
  • @c0d3x27 the regular way to run a shell command in Python is to use the `subprocess` module. There are ways to do it without the explicit import in Python but those are dirty hacks reserved for escaping from a sandboxed environment (it is unlikely to be your homework unless you are learning about security in Python). – jfs Aug 03 '22 at 16:51
  • Yes, confirm this worked just fine without `shell=True` and in fact using `#!/usr/bin/env zsh` instead since this was macos w zsh. – JL Peyret May 10 '23 at 02:08
46

If someone looking for calling a script with arguments

import subprocess

val = subprocess.check_call("./script.sh '%s'" % arg, shell=True)

Remember to convert the args to string before passing, using str(arg).

This can be used to pass as many arguments as desired:

subprocess.check_call("./script.ksh %s %s %s" % (arg1, str(arg2), arg3), shell=True)
Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81
Ponmudi VN
  • 1,493
  • 1
  • 18
  • 22
  • 10
    I recommend using list as arguments like `subprocess.check_call(["./script.ksh", arg1, arg2, arg3], shell=True)` which seems clearer to me and you do not need to care about formatting. – Nerxis Jun 03 '21 at 14:26
37

Actually, you just have to add the shell=True argument:

subprocess.call("sleep.sh", shell=True)

But beware -

Warning Invoking the system shell with shell=True can be a security hazard if combined with untrusted input. See the warning under Frequently Used Arguments for details.

source

zenpoy
  • 19,490
  • 9
  • 60
  • 87
  • 1
    It won't work if `sleep.sh` is not in the `PATH`. And if it *is* in the path and it has the correct shebang then you don't need `shell=True`. See [my answer](http://stackoverflow.com/a/22416147/4279) – jfs Mar 14 '14 at 21:37
15

Make sure that sleep.sh has execution permissions, and run it with shell=True:

#!/usr/bin/python

import subprocess
print "start"
subprocess.call("./sleep.sh", shell=True)
print "end"
Adam Matan
  • 128,757
  • 147
  • 397
  • 562
14

If chmod is not working then you can also try:

import os
os.system('sh script.sh')
# you can also use bash instead of sh
alper
  • 2,919
  • 9
  • 53
  • 102
Harry1992
  • 453
  • 1
  • 5
  • 12
  • 3
    The `os.system` documentation specifically recommends avoiding it in favor of `subprocess` (these days, `subprocess.run()`). If you can't get `subprocess` to work, chances are you'll have trouble with `os.system()` too. – tripleee Jun 02 '20 at 02:03
3

Adding an answer because I was directed here after asking how to run a bash script from python. You receive an error OSError: [Errno 2] file not found if your script takes in parameters. Lets say for instance your script took in a sleep time parameter: subprocess.call("sleep.sh 10") will not work, you must pass it as an array: subprocess.call(["sleep.sh", 10])

  • You have to understand [the actual meaning of `shell=True`](https://stackoverflow.com/questions/3172470/actual-meaning-of-shell-true-in-subprocess) but yes, usually avoiding it is your best play. – tripleee Jun 02 '20 at 02:05