I'm trying to run mkvirtualenv from a bash script, and I'm keep being told that it can't be found - yet it seems that my system can't make up it's mind about whether or not it can find it. Can anyone explain why I can execute it from the terminal, but not from a script?
jimbo#wavefront:~$ locate mkvirtualenv
jimbo#wavefront:~$ which mkvirtualenv
jimbo#wavefront:~$ mkvirtualenv --version
13.1.2
jimbo#wavefront:~$
It's because it's a function attached to the shell. Run this to see it:
$ type mkvirtualenv
Avoid using which to check for binaries/etc. It isn't standardized, isn't always available and is an external binary itself (and so is more expensive than the better choices).
The better choices are type and command.
They are both built-ins, both standardized (at least at their most basic levels) and, because they are built-in, can see shell functions and aliases too.
Related
I'm writing a program that executes short shell one liners (potentially including pipes and background tasks etc), and I'd like to make it "just work" cross platform as much as possible.
For mac/linux the following seems to work well:
shell = os.environ.get("SHELL", "/bin/bash")
subprocess.Popen([shell, "-c", script_content])
However given that on windows:
SHELL isn't usually set
Assuming that bash is installed, a usable bash executable might be found in a variety of different places
What's the best way to make this work as reliably as possible in windows?
Are you looking for C:\Windows\System32\cmd.exe?
And if you are doing cross platorm you could use sys.platorm to find the platform the user is using.
On Window, 'COMSPEC' holds the name of the current command program. You can write unconditional lookup to lookup in the environemnt. Usually, better to take this approach, as in many cases, python script may be executed from 'git-bash', WSL or similar. No need to explicitly program for specific platform.
First Using SHELL
If none, use COMSPEC
See: https://en.wikipedia.org/wiki/COMSPEC
If you have Windows git client installed, you should have git bash, so in a CMD window:
set SHELL="c:\Program Files\Git\bin\bash.exe"
then you can run you python program.
Our environment has a shell script to setup the working area. setup.sh looks like this:
export BASE_DIR=$PWD
export PATH=$BASE_DIR/bin
export THIS_VARIABLE=THAT_VALUE
The user does the following:
% . setup.sh
Some of our users are looking for a csh version and that would mean having two setup files.
I'm wondering if there is a way to do this work with a common python file. In The Hitchhiker's Guide to Python Kenneth Reitz suggests using a setup.py file in projects, but I'm not sure if Python can set environment variables in the shell as I do above.
Can I replace this shell script with a python script that does the same thing? I don't see how.
(There are other questions that ask this more broadly with many many comments, but this one has a direct question and direct single answer.)
No, Python (or generally any process on Unix-like platforms) cannot change its parent's environment.
A common solution is to have your script print the output in a format suitable for the user's shell. E.g. ssh-agent will print out sh-compatible global assignments with -s or when it sees that it is being invoked from a Bourne-compatible shell; and csh syntax if invoked from csh or tcsh or when explicitly invoked with -c.
The usual invocation in sh-compatible shells is $(eval ssh-agent) -- so the text that the program prints is evaluated by the shell where the user invoked this command.
eval is a well-known security risk, so you want to make this code very easy to vet even for people who don't speak much Python (or shell, or anything much else).
If you are, eh cough, skeptical of directly supporting Csh users, perhaps you can convince them to run your sh-compatible script in a Bourne-compatible shell and then exec csh to get their preferred interactive environment. This also avoids the slippery slope of having an ever-growing pile of little maintenance challenges for supporting Csh, Fish, rc, Powershell etc users.
I'm python developer and most frequently I use buildout for managing my projects. In this case I dont ever need to run any command to activate my dependencies environment.
However, sometime I use virtualenv when buildout is to complicated for this particular case.
Recently I started playing with ruby. And noticed very useful feature. Enviourement is changing automatically when I cd in to the project folder. It is somehow related to rvm nad .rvmrc file.
I'm just wondering if there are ways to hook some script on different bash commands. So than I can workon environment_name automatically when cd into to project folder.
So the logic as simple as:
When you cd in the project with folder_name, than script should run workon folder_name
One feature of Unix shells is that they let you create shell functions, which are much like functions in other languages; they are essentially named groups of commands. For example, you can write a function named mycd that first runs cd, and then runs other commands:
function mycd () {
cd "$#"
if ... ; then
workon environment
fi
}
(The "$#" expands to the arguments that you passed to mycd; so mycd /path/to/dir will call cd /path/to/dir.)
As a special case, a shell function actually supersedes a like-named builtin command; so if you name your function cd, it will be run instead of the cd builtin whenever you run cd. In that case, in order for the function to call the builtin cd to perform the actual directory-change (instead of calling itself, causing infinite recursion), it can use Bash's builtin builtin to call a specified builtin command. So:
function cd () {
builtin cd "$#" # perform the actual cd
if ... ; then
workon environment
fi
}
(Note: I don't know what your logic is for recognizing a project directory, so I left that as ... for you to fill in. If you describe your logic in a comment, I'll edit accordingly.)
I think you're looking for one of two things.
autoenv is a relatively simple tool that creates the relevant bash functions for you. It's essentially doing what ruakh suggested, but you can use it without having to know how the shell works.
virtualenvwrapper is full of tools that make it easier to build smarter versions of the bash functions—e.g., switch to the venv even if you cd into one of its subdirectories instead of the base, or track venvs stored in git or hg, or … See the Tips and Tricks page.
The Cookbook for autoenv, shows some nifty ways ways to use the two together.
Just found in the description of virtualenvwraper this topic
It describes exactly what I need.
What i'd like to have is a mechanism that all commands i enter on a Bash-Terminal are wrapped by a Python-script. The Python-script executes the entered command, but it adds some additional magic (for example setting "dynamic" environment variables).
Is that possible somehow?
I'm running Ubuntu and Debian Squeezy.
Additional explanation:
I have a property-file which changes dynamically (some scripts do alter it at any time). I need the properties from that file as environment variables in all my shell scripts. Of course i could parse the property-file somehow from shell, but i prefer using an object-oriented style for that (especially for writing), as it can be done with Python (and ConfigObject).
Therefore i want to wrap all my scripts with that Python script (without having to modify the scripts themselves) which handles these properties down to all Shell-scripts.
This is my current use case, but i can imagine that i'll find additional cases to which i can extend my wrapper later on.
The perfect way to wrap every command that is typed into a Bash Shell is to change the variable PROMPT_COMMAND inside the .bashrc. For example, if I want to do some Python stuff before every command, liked asked in my question:
.bashrc:
# ...
PROMPT_COMMAND="python mycoolscript.py; $PROMPT_COMMAND;"
export $PROMPT_COMMAND
# ...
now before every command the script mycoolscript.py is run.
Use Bash's DEBUG trap. Let me know if you need me to elaborate.
Edit:
Here's a simple example of the kinds of things you might be able to do:
$ cat prefix.py
#!/usr/bin/env python
print "export prop1=foobar"
print "export prop2=bazinga"
$ cat propscript
#!/bin/bash
echo $prop1
echo $prop2
$ trap 'eval "$(prefix.py)"' DEBUG
$ ./propscript
foobar
bazinga
You should be aware of the security risks of using eval.
I don't know of anything but two things that might help you follow
http://sourceforge.net/projects/pyshint/
The iPython shell has some functionality to execute shell commands in the iterpreter.
There is no direct way you can do it .
But you can make a python script to emulate a bash terminal and you can use the beautiful "Subprocess" module in python to execute commnands the way you like
At the beginning of all my executable Python scripts I put the shebang line:
#!/usr/bin/env python
I'm running these scripts on a system where env python yields a Python 2.2 environment. My scripts quickly fail because I have a manual check for a compatible Python version:
if sys.version_info < (2, 4):
raise ImportError("Cannot run with Python version < 2.4")
I don't want to have to change the shebang line on every executable file, if it's possible; however, I don't have administrative access to the machine to change the result of env python and I don't want to force a particular version, as in:
#!/usr/bin/env python2.4
I'd like to avoid this because system may have a newer version than Python 2.4, or may have Python 2.5 but no Python 2.4.
What's the elegant solution?
[Edit:] I wasn't specific enough in posing the question -- I'd like to let users execute the scripts without manual configuration (e.g. path alteration or symlinking in ~/bin and ensuring your PATH has ~/bin before the Python 2.2 path). Maybe some distribution utility is required to prevent the manual tweaks?
"env" simply executes the first thing it finds in the PATH env var. To switch to different python, prepend the directory for that python's executable to the path before invoking your script.
Pretty hackish solution - if your check fails, use this function (which probably could be significantly improved) to determine the best interpreter available, determine if it is acceptable, and if so relaunch your script with os.system or something similar and your sys.argv using the new interpreter.
import os
import glob
def best_python():
plist = []
for i in os.getenv("PATH").split(":"):
for j in glob.glob(os.path.join(i, "python2.[0-9]")):
plist.append(os.path.join(i, j))
plist.sort()
plist.reverse()
if len(plist) == 0: return None
return plist[0]
If you are running the scripts then you can set your PATH variable to point to a private bin directory first:
$ mkdir ~/bin
$ ln -s `which python2.4` ~/bin/python
$ export PATH=~/bin:$PATH
Then when you execute your python script it'll use python 2.4. You'll have to change your login scripts to change your PATH.
Alternatively run your python script with the explicit interpreter you want:
$ /path/to/python2.4 <your script>
#morais: That's an interesting idea, but I think maybe we can take it one step farther. Maybe there's a way to use Ian Bicking's virtualenv to:
See if we're running in an acceptable environment to begin with, and if so, do nothing.
Check if there exists a version-specific executable on the PATH, i.e. check if python2.x exists for x in reverse(range(4, 10)). If so, re-run the command with the better interpreter.
If no better interpreter exists, use virtualenv to try and install a newer version of Python from the older version of Python and get any prerequisite packages.
I have no idea if virtualenv is capable of this, so I'll go mess around with it sometime soon. :)
Here's a solution if you're (1) absolutely set on using shebangs and (2) able to use Autotools in your build process.
I just found last night that you can use the autoconf macro AM_PATH_PYTHON to find a minimal Python 2 binary. The how-to is here.
So, your process would be:
Issue an AM_PATH_PYTHON(2.4) in your configure.ac
Rename all of your .py scripts to .py.in (in my experience, this doesn't confuse vi)
Name all of those Python scripts you want to generate with AC_CONFIG_FILES.
Instead of starting with #!/usr/bin/env python, use #!#PYTHON#
Then your resultant Python scripts will always have an appropriate shebang.
So, you have this solution, at least possible, if not practical.