Change Your $PATH Temporarily

Problem

You want to easily add or remove a directory to or from your $PATH for this session only.

Solution

There are several ways to handle this problem.

You can prepend or append the new directory, using PATH="newdir:$PATH" or PATH="$PATH:newdir", though you should make sure the directory isn’t already in the $PATH.

If you need to edit something in the middle of the path, you can echo the path to the screen, then use your terminal’s kill and yank (copy and paste) facility to duplicate it on a new line and edit it. Or, you can add the “Macros that are convenient for shell interaction” from the readline documentation at http://tiswww.tis.case.edu/php/chet/readline/readline.html#SEC12, specifically:

# edit the path
"C-xp": "PATH=${PATH}eC-eC-aefC-f"
# [...]
# Edit variable on current line.
"M-C-v": "C-aC-k$C-yM-C-eC-aC-y="

Then pressing Ctrl-X P will display the $PATH on the current line for you to edit, while typing any variable name and pressing Meta Ctrl-V will display that variable for editing. Very handy.

For simple cases you can use this quick function (adapted slightly from Red Hat Linux’s /etc/profile):

# cookbook filename: func_pathmunge

# Adapted from Red Hat Linux

function pathmunge {
    if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
        if [ "$2" = "after" ] ; then
            PATH="$PATH:$1"
        else
            PATH="$1:$PATH"
        fi
    fi
}

The egrep pattern looks for the value in $1 between two : or (|) at the beginning (^) or end ($) of the $PATH string. We chose to use a case statement in our function, and to force a leading and trailing : to do the same thing. Ours is theoretically faster since it uses a shell built-in, but the Red Hat version is more concise. Our version is also an excellent illustration of the fact that the if command works on exit codes, so the first if works by using the exit code set by grep, while the second requires the use of the test operator ( [ ] ).

For more complicated cases when you’d like a lot of error checking you can source and then use the following more generic functions:

# cookbook filename: func_tweak_path

#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Add a directory to the beginning or end of your path as long as it's not
# already present. Does not take into account symbolic links!
# Returns: 1 or sets the new $PATH
# Called like: add_to_path <directory> (pre|post)
function add_to_path {
    local location=$1
    local directory=$2

    # Make sure we have something to work with
    if [ -z "$location" -o -z "$directory" ]; then
        echo "$0:$FUNCNAME: requires a location and a directory to add" >&2
        echo "e.g. add_to_path pre /bin" >&2
        return 1
    fi

    # Make sure the directory is not relative
    if [ $(echo $directory | grep '^/') ]; then
        :echo "$0:$FUNCNAME: '$directory' is absolute" >&2
    else
        echo "$0:$FUNCNAME: can't add relative directory '$directory' to the $PATH" >&2
        return 1
    fi

    # Make sure the directory to add actually exists
    if [ -d "$directory" ]; then
        : echo "$0:$FUNCNAME: directory exists" >&2
    else
        echo "$0:$FUNCNAME: '$directory' does not exist--aborting" >&2
        return 1
    fi

    # Make sure it's not already in the PATH
    if [ $(contains "$PATH" "$directory") ]; then
        echo "$0:$FUNCNAME: '$directory' already in $PATH--aborting" >&2
    else
        :echo "$0:$FUNCNAME: adding directory to $PATH" >&2
    fi

    # Figure out what to do
    case $location in
        pre*  ) PATH="$directory:$PATH" ;;
        post* ) PATH="$PATH:$directory" ;;
        *     ) PATH="$PATH:$directory" ;;
    esac

    # Clean up the new path, then set it
    PATH=$(clean_path $PATH)

} # end of function add_to_path


#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Remove a directory from your path, if present.
# Returns: sets the new $PATH
# Called like: rm_from_path <directory>
function rm_from_path {
    local directory=$1

    # Remove all instances of $directory from $PATH
    PATH=${PATH//$directory/}

    # Clean up the new path, then set it
    PATH=$(clean_path $PATH)

} # end of function rm_from_path


#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Remove leading/trailing or duplicate ':', remove duplicate entries
# Returns: echos the "cleaned up" path
# Called like: cleaned_path=$(clean_path $PATH)
function clean_path {
    local path=$1
    local newpath
    local directory

    # Make sure we have something to work with
    [ -z "$path" ] && return 1

    # Remove duplicate directories, if any
    for directory in ${path//:/ }; do
        contains "$newpath" "$directory" && newpath="${newpath}:${directory}"
    done

    # Remove any leading ':' separators
    # Remove any trailing ':' separators
    # Remove any duplicate ':' separators
    newpath=$(echo $newpath | sed 's/^:*//; s/:*$//; s/::/:/g')

    # Return the new path
    echo $newpath

} # end of function clean_path


#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Determine if the path contains a given directory
# Return 1 if target is contained within pattern, 0 otherwise
# Called like: contains $PATH $dir
function contains {
    local pattern=":$1:"
    local target=$2

    # This will be a case-sensitive comparison unless nocasematch is set
    case $pattern in
        *:$target:* ) return 1;;
        *           ) return 0;;
    esac
} # end of function contains

Use as follows:

$ source chpath

$ echo $PATH
/bin:/usr/bin:/usr/local/bin:/usr/bin/X11:/usr/X11R6/bin:/home/jp/bin

$ add_to_path pre foo
-bash:add_to_path: can't add relative directory 'foo' to the $PATH

$ add_to_path post ~/foo
-bash:add_to_path: '/home/jp/foo' does not exist--aborting

$ add_to_path post '~/foo'
-bash:add_to_path: can't add relative directory '~/foo' to the $PATH

$ rm_from_path /home/jp/bin

$ echo $PATH
/bin:/usr/bin:/usr/local/bin:/usr/bin/X11:/usr/X11R6/bin

$ add_to_path /home/jp/bin
-bash:add_to_path: requires a location and a directory to add
e.g. add_to_path pre /bin

$ add_to_path post /home/jp/bin

$ echo $PATH
/bin:/usr/bin:/usr/local/bin:/usr/bin/X11:/usr/X11R6/bin:/home/jp/bin

$ rm_from_path /home/jp/bin

$ add_to_path pre /home/jp/bin

$ echo $PATH
/home/jp/bin:/bin:/usr/bin:/usr/local/bin:/usr/bin/X11:/usr/X11R6/bin

Discussion

There are four interesting things about this problem and the functions presented in func_tweak_path in the Solution.

First, if you try to modify your path or other environment variables in a shell script, it won’t work because scripts run in subshells that go away when the script terminates, taking any modified environment variables with them. So instead, we source the functions into the current shell and run them from there.

Second, you may notice that add_to_path post ~/foo returns “does not exist” while add_to_path post'~/foo' returns “can’t add relative directory.” That’s because ~/foo is expanded by the shell to /home/jp/foo before the function ever sees it. Not accounting for shell expansion is a common mistake. Use the echo command to see what the shell will actually pass to your scripts and functions.

Next, you may note the use of lines such as echo"$0:$FUNCNAME:requires a directory toadd">&2. $0:$FUNCNAME is a handy way to identify exactly where an error message is coming from. $0 is always the name of the current program (-bash in the solution’s example, and the name of your script or program in other cases). Adding the function name makes it easier to track down problems when debugging. Echoing to >&2 sends the output to STDERR, where runtime user feedback, especially including warnings or errors, should go.

Finally, you can argue that the functions have inconsistent interfaces, since add_to_path and remove_from_path actually set $PATH, while clean_path displays the cleaned up path and contains returns true or false. We might not do it that way in production either, but it makes this example more interesting and shows different ways to do things. And we might argue that the interfaces make sense given what the functions do.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.130.24