166

I'm trying to write (what I thought would be) a simple bash script that will:

  1. run virtualenv to create a new environment at $1
  2. activate the virtual environment
  3. do some more stuff (install django, add django-admin.py to the virtualenv's path, etc.)

Step 1 works quite well, but I can't seem to activate the virtualenv. For those not familiar with virtualenv, it creates an activate file that activates the virtual environment. From the CLI, you run it using source

source $env_name/bin/activate

Where $env_name, obviously, is the name of the dir that the virtual env is installed in.

In my script, after creating the virtual environment, I store the path to the activate script like this:

activate="`pwd`/$ENV_NAME/bin/activate"

But when I call source "$activate", I get this:

/home/clawlor/bin/scripts/djangoenv: 20: source: not found

I know that $activate contains the correct path to the activate script, in fact I even test that a file is there before I call source. But source itself can't seem to find it. I've also tried running all of the steps manually in the CLI, where everything works fine.

In my research I found this script, which is similar to what I want but is also doing a lot of other things that I don't need, like storing all of the virtual environments in a ~/.virtualenv directory (or whatever is in $WORKON_HOME). But it seems to me that he is creating the path to activate, and calling source "$activate" in basically the same way I am.

Here is the script in it's entirety:

#!/bin/sh

PYTHON_PATH=~/bin/python-2.6.1/bin/python

if [ $# = 1 ]
then
    ENV_NAME="$1"
    virtualenv -p $PYTHON_PATH --no-site-packages $ENV_NAME
    activate="`pwd`/$ENV_NAME/bin/activate"

    if [ ! -f "$activate" ]
    then
        echo "ERROR: activate not found at $activate"
        return 1
    fi

    source "$activate"
else
    echo 'Usage: djangoenv ENV_NAME'
fi

DISCLAIMER: My bash script-fu is pretty weak. I'm fairly comfortable at the CLI, but there may well be some extremely stupid reason this isn't working.

Chris Lawlor
  • 41,304
  • 11
  • 45
  • 67

3 Answers3

236

If you're writing a bash script, call it by name:

#!/bin/bash

/bin/sh is not guaranteed to be bash. This caused a ton of broken scripts in Ubuntu some years ago (IIRC).

The source builtin works just fine in bash; but you might as well just use dot like Norman suggested.

guns
  • 9,800
  • 3
  • 36
  • 36
  • This solution was originally a comment in Norman Ramsey's answer. Since this is what actually fixed the problem, I've changed this to be the 'accepted answer' – Chris Lawlor Dec 17 '09 at 22:20
196

In the POSIX standard, which /bin/sh is supposed to respect, the command is . (a single dot), not source. The source command is a csh-ism that has been pulled into bash.

Try

. $env_name/bin/activate

Or if you must have non-POSIX bash-isms in your code, use #!/bin/bash.

Norman Ramsey
  • 188,173
  • 57
  • 343
  • 523
  • 1
    That fixes it. (changing /bin/sh to /bin/bash). For some reason the environment is not activated in the CLI when the script finishes, but that's a minor problem. – Chris Lawlor Mar 22 '09 at 00:29
  • 8
    According to the [Bash manual](https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html#index-source) `source` is a synonym for `.`. – Richard Hansen Nov 11 '15 at 06:00
  • 1
    I came across this when using a docker container with entrypoint like this, `/bin/sh -c '/path/to/script.sh'`. Even though my script was a bash script, source failed to source the exports. But "." worked! – Nikhil Owalekar Sep 26 '18 at 10:31
33

In Ubuntu if you execute the script with sh scriptname.sh you get this problem.

Try executing the script with ./scriptname.sh instead.

Addison
  • 5,106
  • 2
  • 31
  • 49
Madhu
  • 331
  • 3
  • 2