Python not working on a bash script but working on the terminal. How to solve it?

185 Views Asked by At

I am trying to run a Python program through a Bash script. When I execute the python file by shell it works with no problem, but when I try to execute the script, the python keyword is not recognized.

I have a fresh Ubuntu 23.10 installation and I added, on top of the ~/.bashrc file, the following line:

alias python=python3

How to solve this problem?

Toy example

For sake of simplicity I made a toy example that displays the same behavior.

I have an hello-world.py file, whose content is simply

print("Hello World!")

If I run it with the shell ($ python hello-world.py), it works smoothly:

Hello World!

Now I created a script start.sh whose content is:

#!/bin/bash

python hello-world.py

But if I execute it $ ./start.sh I get the following error:

./start.sh: line 3: python: command not found

3

There are 3 best solutions below

4
juanpa.arrivillaga On BEST ANSWER

So, the issue is that your shebang isn't running bash as a login shell (which is pretty normal). You are aliasing python to python3 in your .bashrc, but that will only get run in a login shell. So try adding:

#!/bin/bash -l 

to your shebang.

Although, to be clear, I would consider this a hack. I would suggest that ideally you just use python3 in the shell script.

Or, create a symlink. If you are on ubuntu, you can use python-is-python3:

https://packages.ubuntu.com/focal/python-is-python3

0
KamilCuk On

.bashrc is not sourced when running noninteractive shell.

Aliases are disabled in noninteractive shell (by default).

A script is noninteractive, so these settings are ignored.

Refer to bash documentation on startup files.

how to solve it?

Create a symlink in a directory added to PATH with the name python that links to python3 executable. For example /usr/local/bin or ~/.local/bin.

Use your linux distribution specific settings, like apt-get install python-is-python3.

Either way, use python3 in your scripts anyway.

0
duise On

There are basically at least five kinds of Bash shell invocations:

  1. interactive + login

  2. interactive + non-login

  3. non-interactive + --login option

  4. non-interactive + remote-login (via rshd or sshd)

  5. non-interactive + non-login

The descriptions are taken from the Bash Manual, 6.2. Bash Startup Files:

1+3 )

When Bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable.

2 )

When an interactive shell that is not a login shell is started, Bash reads and executes commands from ~/.bashrc, if that file exists.

4 )

Bash attempts to determine when it is being run with its standard input connected to a network connection, as when executed by the historical remote shell daemon, usually rshd, or the secure shell daemon sshd. If Bash determines it is being run non-interactively in this fashion, it reads and executes commands from ~/.bashrc, if that file exists and is readable. It will not do this if invoked as sh

5 )

When Bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV in the environment, expands its value if it appears there, and uses the expanded value as the name of a file to read and execute

I've descibed a way to handle all five invocations with minimum effort startup files. Please find the details here.

Basically you create three small Bash startup files in your HOME directory:

~/.bash_env:

[ -n "$BASH_ENV_VERSION" ] && return 0
BASH_ENV_VERSION=.....
...
[ -f "$HOME/.bash_aliases" ] && source "$HOME/.bash_aliases"
...

~/.bashrc:    

if [ -z "$POSIXLY_CORRECT" ]; then
    [ -z "$BASH_ENV" ] && export BASH_ENV="$HOME/.bash_env"
    source "$BASH_ENV"  
fi

~/.bash_profile:

 [ -f "$HOME/.profile" ] && source "$HOME/.profile"
 [ -f "$HOME/.bashrc" ] && source "$HOME/.bashrc"

The ~/.bash_env is where all your variables and functions go which you like to have at hand no matter how bash has been invoked. The first two lines are a guard against multiple inclusions. No shebangs needed!

Finally, put your Bash alias definitions in the file ~/.bash_aliases

This can be extended to cover startup files for other login shells and Bash invocations in POSIX mode, e.g. when started as sh or with the --posix command line option. In that case you may want to use the ENV variable instead of BASH_ENV and define it in ~/.profile instead of ~/.bash_profile. I intentionally left that out to not make things too complicated.