Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

If I have a colon-separated path list, much like $PATH, but not neccessarily $PATH.

I want to search that list for a specific file name. However, I only want the first matching path.

I have considered the following linux commands:

  • which: only works for binary, and only works with $PATH variable
  • whereis: works with particular kinds of files, and only works with $PATH variable
  • find: does not support colon-separated path lists, and returns multiple results

Here are some things I have tried:

  1. I have tried to use whereis by the following strategy

    env WHEREIS="`which whereis`" PATH="$MY_PATH_LIST" $WHEREIS "$TARGET_FILE"
    

    and this almost works. However, it does not seem to return results for arbitrary file types. It also returns multiple results, and in an awkward format.

  2. I could get which to work by

    env WHICH="`which which`" PATH="$MY_PATH_LIST" $WHICH "$MY_TARGET_FILE"
    

    if there was a command-line option to force it to allow non-executables.

  3. I then tried to solve the problem with find. First I used regular expressions to expand the path list (I replace colons with spaces). Then I invoke find, and it works correctly. However, it searches all of the paths. I cannot seem to find a way to tell it to stop the search early if it finds a good result.

    I did get this to work

     find ${MY_PATH_LIST//[:]/ } -name "$MY_TARGET_FILE" | head -n 1
    

    but it takes a long time to complete, because find is still doing an exhaustive search.

    I need this to execute faster (exit on first result), because it would be run many times, with different parameters.

Does anyone have a better solution to suggest?

Note that if all fails, I can write a non-bash solution. Write now I'm hoping for a simple solution using existing tools.

share|improve this question
up vote 3 down vote accepted

You could just use a script that simply tests (-e) for existence of a file and stops when the first one has been found:

#!/bin/bash
[[ $# -gt 0 ]] || { echo "Usage: $0 <filename> [pathspec]" >&2 ; exit 1 ; }
if [[ $# -gt 1 ]] ; then
        P="$2"
else
        P="$PATH"
fi

IFS=:
for DIR in $P ; do
        if [[ -e "$DIR/$1" ]] ; then
                echo "$DIR/$1"
                exit 0
        fi
done

Example:

$ ./search.sh 
Usage: ./search.sh <filename> [pathspec]

$ ./search.sh ls
/Users/danielbeck/bin/ls

$ ./search.sh pwd
/bin/pwd

$ ./search.sh ls /bin
/bin/ls

$ ./search.sh ls /usr/bin:/bin
/bin/ls
share|improve this answer
    
Thanks, Daniel. This works. I am surprised that there isn't a built-in command for this purpose. I'm going to wait a bit to see if there are any interesting comments. So far, it looks like this one will be going to you. :-) – Kevin A. Naudé Dec 20 '12 at 11:35

Use head to get the first result of the find command.

find `echo colon:separated:directories | sed 's/:/ /g'` -name filename | head -n1

Or in a function:

$ function findin { find `echo $1 | sed 's/:/ /g'` -name $2 | head -n1; }
$ findin this:that:other filename
share|improve this answer

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .