Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I'm trying to create aliases for commands I'm typing a lot. Because I'm working on a big project built on Laravel I need to use the command line tool artisan fairly often. However, I don't often want to be in the root directory with that file.

I don't usually have to recurse upwards in scripts I've written, so I'm a little lost. My first thought is to attempt to use a command like find . -name artisan and if it finds nothing, to cd .. and check again until the current working directory is /. Then I want to be able to pass arguments from the shell script to the final, found copy of artisan.

Is there a linux/unix/osx terminal utility/convention that would take care of this?

Edit:

Sorry, I should have been clear about this before, but I didn't think about it.

artisan is a CLI interface to the Laravel PHP framework. I work in version 3, but if I start another project in it I will probably use version 4. Many breaking changes exist between these versions, but more importantly the artisan script in the root of the project is just a bootstrap script to the code living within the framework.

EG, because it has local, version-sensitive dependencies to a paths.php reference, which tells it where other resources are like the framework itself, I don't think I can move this to a bin directory and symlink to it.

Yes I can symlink down to the script from sub-directories, but I was looking for something a bit more automatic than that. For instance, how does Git go about locating the repository directory from a subdirectory? I know it must recurse upward in the tree to locate it, but as its in C I am at a loss for how to find it, or implement it in Shell/Bash script.

share|improve this question
    
By the way, here's an example of Git awareness, used for Bash's prompt, based on Git's bash completion –  slhck Jan 17 '13 at 21:15
add comment

3 Answers

up vote 3 down vote accepted

You could look for the file in the current directory, and if it's not found, work your way up through the filesystem until either you find the file or you reach the root. For example:

#!/bin/bash

wanted=$1

dir=.
while [[ ! -x "$dir/$wanted" ]]; do
    if [[ "$dir" -ef "../$dir" ]]; then
        echo "Could not find '$wanted'." 1>&2
        exit 2
    fi
    dir="../$dir"
done

echo "Found '$dir/$wanted'."

Here, we detect that we've reached the root by noticing that . and .. refer to the same directory.

share|improve this answer
1  
Excellent! I was going to write this if somebody else hadn’t. Notes: (1) I suggest wanted=$(basename $0), so the script can be called artisan; (2) add a check for [[ "$0" -ef "$dir/$wanted" ]], to handle the case where the user runs the script from the bin directory (or a subdirectory of it); and (3) remove (comment out) the final echo and replace it with an exec. –  Scott Jan 18 '13 at 1:19
    
Mavit and @Scott Thank you for your help. I'm going to post the final script as my own answer so that someone else with a similar question can have a copy. –  Robert K Jan 18 '13 at 14:21
add comment

Thanks to Mavit's answer and Scott's comments, I've come up with a nice working solution. I use this on Mac OS X 10.6.8, but it should be very portable.

#!/bin/bash

wanted=$(basename $0)

dir=.
while [[ ! -x "$dir/$wanted" ]]; do
    if [[ "$dir" -ef "../$dir" ]]; then
        echo "$wanted is not in the current directory or any of its parents" 1>&2
        exit 2
    fi

    dir="../$dir"
done

# Find the absolute path to the command
dir=$(cd -- "$dir" && pwd)

if [[ "$dir/$wanted" -ef "$0" ]]; then
    echo "Cannot call self."
    exit 2
fi

# Execute the command and pass in $1... parameters to it.
exec "$dir/$wanted" "$@"
share|improve this answer
    
The last line should be exec $dir/$wanted "$@", to handle the case where the script is invoked with an argument with space(s) in it (e.g., artisan 'foo bar'). To be doubly safe, exec "$dir/$wanted" "$@", so you can rename the script to have a space in its name, or handle the case where the full path of the directory (output from pwd) contains space(s). (Similarly, basename "$0".) And did you include the check to prevent the script from executing itself? I don’t see it. –  Scott Jan 18 '13 at 15:21
    
@Scott Where do you suggest that I put that check? –  Robert K Jan 18 '13 at 16:56
    
It looks like you figured it out. –  Scott Jan 18 '13 at 17:55
add comment

If you're looking for one particular command (and you know where it should be), why not either call it with the full path like /usr/local/bin/path/to/artisan, or add artisan's directory to your PATH variable.

share|improve this answer
    
What if I have multiple projects with different versions of the framework? Between Laravel 3 and 4 are many breaking changes, and both versions expect a paths.php file to be in their local route. –  Robert K Jan 17 '13 at 20:57
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.