Difference between two quite-similar Terminal commands

bashcommand lineterminal

I have a script that I run as part of my development (it's the activation script for a Python venv environment). The way the documentation suggests that we run it is by going to the folder containing our venv folder and running . venv/bin/activate. This works properly (second command in my example).

However, what I would normally have run is ./venv/bin/activate, i.e. providing a relative path to the activate script (first command in my example). This doesn't work at all (although I am not surprised, because the activate file doesn't have "execute" permissions attached to it).

My-MBP:flask-tutorial stephendewey$ ./venv/bin/activate
-bash: ./venv/bin/activate: Permission denied
My-MBP:flask-tutorial stephendewey$ . venv/bin/activate
(venv) My-MBP:flask-tutorial stephendewey$

What is the command that works (. venv/bin/activate) actually doing? I've never seen syntax like that before.

Best Answer

The . command is an alias for source so the two commands are really

./venv/bin/activate

and

source venv/bin/activate

Also note that for the system to actually process a file it needs the absolute path ie one beginning with /

Both files names here are relative ones ie ones that can only be understood with the knowledge of the current directory which is the variable $(CWD)

The two file names expand to $(CWD)/./venv/bin/activate and $(CWD)/venv/bin/activate . is the current directory and so both are $(CWD)/venv/bin/activate

The difference between running a command directly or via source is that if run directly as in the first command bash creates a new sub shell and runs the command in that and the commands in the script only affect that sub shell and when the script rends that sub shell is closed and all the changes to environment are lost. source, however, runs the command in the current shell and any changes to the environment remain after the script finishes as if the commands in the script were typed into the current shell.

The activate script (I assume is from Python virtual environment management) works by changing the $PATH so that the correct python environment is found when you use python script.py To do this you need to alter your current $PATH and so the activate script needs to be run using source.

Also see https://askubuntu.com/questions/182012/is-there-a-difference-between-and-source-in-bash-after-all and https://superuser.com/questions/176783/what-is-the-difference-between-executing-a-bash-script-vs-sourcing-it

Not that running a command requires the command to be executable. The command to be run is always a file and that file has to be marked as executable by the system. That is the executable flag has to be set on the file permissions which you can see by ls -l venv/bin/activate

source however is in the current shell and just reads the file as text and then executes the commands it sees, So this file only needs to be readable. For more on that see https://unix.stackexchange.com/questions/291404/why-does-bashs-source-not-need-the-execution-bit I like this answer

It's more of a convenience thing: Let the system run it for me directly if the the bit is set, otherwise I need to do it indirectly