3

I'm sandboxing all of my projects with rvm and bundler, so they are all nicely standalone and all of their dependencies can be kept in source control. IN one of them, i run the project with a bin that is kept in the /bin folder. I therefore need to add this to the PATH variable. However, i'd like this to be done in a file that is in the project route, so it's done automatically.

Here's my script, which is in a file called ".runme":

# .runme
# add local bin folder to PATH unless it's already in there
function __bin_dir {
  echo "`pwd`/bin"
}
function __have_bin {
  echo $PATH | grep "^$(__bin_dir)"
}
[ "$(__have_bin)" == "" ] && export PATH=$(__bin_dir):$PATH

Can i get this to run automatically on going to the folder it's in?

Max Williams
  • 32,435
  • 31
  • 130
  • 197

3 Answers3

3

If you're able to add stuff to the .bashrc file of every user that needs this functionality, you can probably hook into the cd operation to check for what you need.

There's another SO question about how to hook into the cd operation: Is there a hook in Bash to find out when the cwd changes?

I'm not familiar with RVM, but they seem to have some documentation on hooking into cd: https://rvm.beginrescueend.com/workflow/hooks/

Community
  • 1
  • 1
Kannan Goundan
  • 4,962
  • 3
  • 24
  • 31
  • Thanks Kannan - that after_cd hook looks promising. One problem is that whatever code i put in there would get run anytime someone cd'd to any folder, not just the project folder, so i'd need to add some sort of test to make sure it only happened in the project folder. I'm now swaying towards NOT making it automatic and instead in some obvious setup script in the project root. – Max Williams Apr 08 '11 at 12:53
  • You could make a script with the smallest name you can bear to type, and when you cd into your new dir, type 't' (or whatever), the t script can see if it's in a directory of interest, and if yes, then execute some code. Good luck. – shellter Apr 08 '11 at 17:26
  • Yeah, you'd need to add a test (check if there's a ".runme" file in the current directory and if so, run it). But overall I'd prefer not making it automatic. – Kannan Goundan Apr 12 '11 at 06:29
  • +1 for the link to the other SO question, notably the `$COMMAND_PROMPT` approach; don't forget that `pushd` changes directories, too; the RVM link is dead now. `rvm` _does_ redefine the `cd` function, so beware of conflicts. – mklement0 Apr 14 '14 at 03:36
1

Another trick is to put the function in your PS1, like

export PS1='...\[$(.runme)\]

(replace ... with whatever you already have in your PS1). This will run the check at each new prompt.

You'll want to make the command run as fast as possible, though, because its runtime will delay the displaying of your prompt. A good start for this would be to make it a bash function and only use bash builtins, so that it doesn't have to fork to run any external programs (like grep).

asmeurer
  • 86,894
  • 26
  • 169
  • 240
  • 1
    That works, but it's probably cleaner to use Bash's `$PROMPT_COMMAND` variable: "If set, the value is interpreted as a command to execute before the printing of each primary prompt ($PS1)." - http://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html – mklement0 Jun 10 '15 at 14:53
1

Here's a less intrusive alternative by way of a simple alias:

alias lx='PATH="./bin:$PATH"'

You can then use lx (Local eXecution) in any folder that has a bin subfolder to execute scripts there without a path; e.g., to execute script ./bin/start-server from the current directory, run:

 lx start-server

If you want to enable tab-completion based on the local executables, add the following to your bash profile (tested on OSX and Linux):

# Install the custom tab-completion function defined below.
complete -F _complete_lx -- lx


# Command-completion function for lx: tab-completes the executables in 
# the ./bin subfolder.
_complete_lx() {

    # Set the directory in which to look for local executables.
  local localExeDir='./bin'

      # Get the current command-line token.
  local token=${COMP_WORDS[$COMP_CWORD]}
  local tokenLen=${#token}

  # Find all local executables.
  read -d ' ' -ra localExes < <(find "$localExeDir" -maxdepth 1 -type f -perm -a=x -exec basename -a {} +;)

  # Filter the list of local executables
  # based on the current command-line token.

  # Turn case-insensitive matching temporarily on, if necessary.
  local nocasematchWasOff=0
  shopt nocasematch >/dev/null || nocasematchWasOff=1
  (( nocasematchWasOff )) && shopt -s nocasematch

  COMPREPLY=()
  for localExe in "${localExes[@]}"; do
    if [[ ${localExe:0:$(( tokenLen ))} == "$token" ]]; then
      COMPREPLY+=( "$localExe" )
    fi
  done

  # Restore state of 'nocasematch' option, if necessary.
  (( nocasematchWasOff )) && shopt -u nocasematch

}
mklement0
  • 382,024
  • 64
  • 607
  • 775