Ubuntu – Should I execute bash scripts from python with `bash` or `./`

bashpythonscripts

I wrote a Python script that executes some bash scripts like this:

cmd = subprocess.Popen(['bash', 'script.sh'])

That way it executes the script although I didn't set execute permissions with chmod. I can also write in the terminal bash script.sh and it works the same. I know scripts can be executed by prepending dot slash ./ to them like this ./script.sh.

So, which way should I use in my Python script? Why?

Best Answer

First, regarding the dot in ./script.sh, this indicates that script.sh can be found in the working directory, it means something like "look for script.sh here, in this directory". This is known as dot slash, and it is well explained here.

I see three options:

  1. If you intend to restrict the file to only run if it is in the user's current working directory when he runs your Python script, then use the dot slash. However, this doesn't seem like the best idea: it will lead to exceptions most of the time.

  2. If you intend to restrict your shell script to run only if it is in the same directory as your Python script, then you would need to find out your Python script's directory and prefix it to the name of your script.

  3. If you intend to place your shell script elsewhere in your PATH, don't use the dot slash.

Now, sending "bash" as an argument to the Popen constructor seems more like a matter of preference. If you don't want to send it as an argument, then you will need to add a shebang to the shell script, e.g. #!/bin/bash, and you will need to add execute permissions to it. These are perfectly acceptable things to do.

One benefit of doing it this way is if later one changes the shell interpreter, it won't be necessary to modify every single call to that script in who knows how many Python scripts.

And one can just call the script like:

cmd = subprocess.call('script.sh')
# or
cmd = subprocess.call(sdir + 'script.sh')
# Where sdir stands for the script's directory,
# which you would need to find before, as in option #2.

Notes:

Even if your script runs without a shebang normally (in which case it almost certainly gets executed by dash and not by bash on Debian based distros), when calling it from python like this, it will fail and raise an OSError exception with errno 8 (errno.ENOEXEC).

If it's really necessary, use Popen.

Related Question