3

The utility which will find a program on the path but what about an arbitrary data file?

I must be searching for the wrong stuff but I can't find any way to do this with a standard utility so I thought of writing a little Bash script to do it.

The $PATH environment variable is separated by colons, but trying to set IFS=':' and then iterate over the results is not working for me. Here's what I've got so far:

IFS=':' DIRS=($PATH)
for d in $DIRS; do echo $d; done

At this point it only outputs the first entry in my path rather than all of them.

Thoughts? If there's already a standard command that does this then there's no reason to write a script ...

Neil C. Obremski
  • 18,696
  • 24
  • 83
  • 112

4 Answers4

10

Using this, using bash parameter expansions and find:

find ${PATH//:/\/ } -name 'file'
Gilles Quénot
  • 173,512
  • 41
  • 224
  • 223
4

The error is not with the assignment, but with not looping over the array.

IFS=: dirs=($PATH)
for dir in "${dirs[@]}"; do
    echo "$dir"
done

works for me; or more succinctly

printf '%s\n' "${dirs[@]}"

Like you discovered, just $dirs only returns the first item from the array.

To actually traverse these directories looking for a particular file, maybe try

desired_file_name=$1  # or whatever
for dir in "${dirs[@]}"; do
    test -e "$dir/$desired_file_name" || continue
    echo "$0: found $dir/$desired_file_name" >&2
done

Another approach is

find "${dirs[@]}" -name "$desired_file_name"

(You should prefer lower case for your private variables, so I changed DIRS to dirs. Less shouting is good for the eyes, too.)

tripleee
  • 175,061
  • 34
  • 275
  • 318
2

To find a file named $name on your PATH using a bash loop:

oldIFS=$IFS
IFS=:
for d in $PATH
do
    [ -f "$d/$name" ] && echo "Found $d/$name"
done
IFS=$oldIFS

On many systems, such as debian, which is just a shell script and it uses a very similar loop. Have a look at less /usr/bin/which.

John1024
  • 109,961
  • 14
  • 137
  • 171
2

A few of things going on here:

  1. IFS=':' DIRS=($PATH)

    bash will expand the variable before setting IFS and DIRS, so it's too late by then. You'll need something crazy like

    dirs=()
    while IFS= read -r -d: dir || [ "$dir" ]; do dirs+=("$dir"); done <<<"$PATH"
    

-- nope, that was wrong. First the $PATH variable is expanded, then IFS is set, then the DIRS element is split using IFS because it is unquoted.

  1. for d in $DIRS; do echo $d; done

    To iterate over all the elements of an array, you need

    for d in "${dirs[@]}" ...
    

    With an array, $DIRS is equal to ${DIRS[0]}, i.e. the first entry.

  2. Don't use ALLCAPS varnames. It's too easy to overwrite a crucial system variable.

  3. Quote your variables unless you know exactly what will happen if you don't.

glenn jackman
  • 238,783
  • 38
  • 220
  • 352