Skip to main content
Better discussion of relative paths in `PATH` issues
Source Link
cjs
  • 728
  • 7
  • 15

There are a few things that it's nice to do when adding a directory pathsome situations where it using (which hereon I'll referPATH=/a/b:$PATH might be considered the "incorrect" way to asadd a "dir" to avoid confusion)path to PATH:

  1. Check to see that the dir isAdding a path that's not actually valid.
  2. Don't add a dir twicedirectory.
  3. Canonicalize the dir you're adding to ensure you're not adding an alias for an existing dirAdding a path that's already in PATH in the same form.1
  4. IfAdding a dir isrelative path (since the actual directory searched would change as you change the current working directory).
  5. Adding a path that's already in PATH but not at the frontin a different form (i.e., force itan alias due to using symlinks or ..).
  6. If you avoid doing 4, not moving the frontpath to ensure the programs in that dir take priority over othersfront of the same name inPATH when it's intended to override other dirsentries in PATH.

This (Bash-only) function does the "right thing" in the above situations (with an exception, see below), returns proper error codes, and prints nice messages for humans. (TheThe error codes and messages can be disabled when they're not wanted.)

1 Technically The exception is that this function does not canonicalize paths added to PATH via other means, you also should be canonicalizing all existing directoriesso if a non-canonical alias for a path is in PATH, but I've been too lazythis will add a duplicate. Trying to do thatcanonicalize paths already in PATH is a dicey proposition since a relative path has an obvious meaning when passed to prepath but when already in the code included herepath you don't know what the current working directory was when it was added.

There are a few things that it's nice to do when adding a directory path (which hereon I'll refer to as a "dir" to avoid confusion) to PATH:

  1. Check to see that the dir is actually valid.
  2. Don't add a dir twice.
  3. Canonicalize the dir you're adding to ensure you're not adding an alias for an existing dir in PATH.1
  4. If a dir is already in PATH but not at the front, force it to the front to ensure the programs in that dir take priority over others of the same name in other dirs.

This (Bash-only) function does the above, returns proper error codes, and prints nice messages for humans. (The error codes and messages can be disabled when they're not wanted.)

1 Technically, you also should be canonicalizing all existing directories in PATH, but I've been too lazy to do that in the code included here.

There are some situations where it using PATH=/a/b:$PATH might be considered the "incorrect" way to add a path to PATH:

  1. Adding a path that's not actually a directory.
  2. Adding a path that's already in PATH in the same form.
  3. Adding a relative path (since the actual directory searched would change as you change the current working directory).
  4. Adding a path that's already in PATH in a different form (i.e., an alias due to using symlinks or ..).
  5. If you avoid doing 4, not moving the path to the front of PATH when it's intended to override other entries in PATH.

This (Bash-only) function does the "right thing" in the above situations (with an exception, see below), returns error codes, and prints nice messages for humans. The error codes and messages can be disabled when they're not wanted.

The exception is that this function does not canonicalize paths added to PATH via other means, so if a non-canonical alias for a path is in PATH, this will add a duplicate. Trying to canonicalize paths already in PATH is a dicey proposition since a relative path has an obvious meaning when passed to prepath but when already in the path you don't know what the current working directory was when it was added.

Source Link
cjs
  • 728
  • 7
  • 15

There are a few things that it's nice to do when adding a directory path (which hereon I'll refer to as a "dir" to avoid confusion) to PATH:

  1. Check to see that the dir is actually valid.
  2. Don't add a dir twice.
  3. Canonicalize the dir you're adding to ensure you're not adding an alias for an existing dir in PATH.1
  4. If a dir is already in PATH but not at the front, force it to the front to ensure the programs in that dir take priority over others of the same name in other dirs.

This (Bash-only) function does the above, returns proper error codes, and prints nice messages for humans. (The error codes and messages can be disabled when they're not wanted.)

prepath() {
    local usage="\
Usage: prepath [-f] [-n] [-q] DIR
  -f Force dir to front of path even if already in path
  -n Nonexistent dirs do not return error status
  -q Quiet mode"

    local tofront=false errcode=1 qecho=echo
    while true; do case "$1" in
        -f)     tofront=true;       shift;;
        -n)     errcode=0;          shift;;
        -q)     qecho=':';          shift;;
        *)      break;;
    esac; done
    # Bad params always produce message and error code
    [[ -z $1 ]] && { echo 1>&2 "$usage"; return 1; }

    [[ -d $1 ]] || { $qecho 1>&2 "$1 is not a directory."; return $errcode; }
    dir="$(command cd "$1"; pwd -P)"
    if [[ :$PATH: =~ :$dir: ]]; then
        $tofront || { $qecho 1>&2 "$dir already in path."; return 0; }
        PATH="${PATH#$dir:}"        # remove if at start
        PATH="${PATH%:$dir}"        # remove if at end
        PATH="${PATH//:$dir:/:}"    # remove if in middle
    fi
    PATH="$dir:$PATH"
}

1 Technically, you also should be canonicalizing all existing directories in PATH, but I've been too lazy to do that in the code included here.