Shell functions to manipulate the PATH
From: Enrique Perez-Terron (enrio_at_online.no)
Date: 09/03/05
- Previous message: kermit: "Re: Kernel Install Question: Is "make install" really necessary ?"
- Next in thread: aaron.toponce_at_gmail.com: "Re: Shell functions to manipulate the PATH"
- Reply: aaron.toponce_at_gmail.com: "Re: Shell functions to manipulate the PATH"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Sat, 03 Sep 2005 12:00:16 +0200
I would like to suggest to distribution maintainers that they include
functions similar to the ones below in some file under /etc/profile.d.
The reason is mainly to present a friendlier interface to newbies that
often have to fiddle with the PATH variable before they get everything
properly set up.
Yes, quite often when the newbies want to change the PATH it is misguided,
because they are trying to run a /sbin command as ordinary users. When
they try to figure out why it does not work, they learn about the PATH...
But you can't improve this by making it hard to change the PATH. Instead,
let the users get the PATH set as they wanted it with less effort, and
they will have more energy left for understanding what happens next. If
they need privileges for what they try next, the error message will
hopefully hint about that. "bash: lsof: no such command" is not very
explicit here.
On today's computers, the PATH tends to be quite long. Having to type it
in from scratch is quite "unfriendly" because no newbie remembers the
exact spelling of all those variables, and, if he has hosed the PATH
variable, commands like ls may just not be available.
Quite certainly, as soon as you get your ears a little dryer, you might
not need this much. But when you try to explain things to newbies, and
want to show them how it works, you are essentially showing them that
"Linux/Unix is hard, error-prone, and unfriendly".
Even the simplest of tasks, determining what is in your PATH, typing "echo
$PATH" shows you a string that most often breaks across several lines in
the command window, breaking in the middle of a word, and with all the
directories run together with no air between to visually separate them. It
looks more like the maze of wires under your car's hood than like the
dashboard in front of the driver's seat.
There is one good reason writing such scripts have not been very
successfull in the past, and that is that if you hose you PATH, and sed,
cut, and all the others become unavailable, then your script will have to
have its own PATH setting, or use fixed paths. Then the script is less
poratble and does not work when you need it most, as when you created a
chroot jail and placed some commands in the wrong directory or nowhere at
all.
The script below needs bash to run, but no other tools. If the file
containing the functions has been sourced when you started the shell, the
functions are there quite independent of the PATH and quite independent of
any other commands.
I bet quite a few people have written similar scripts with similar, yet
different names and options. The sad thing is that those capable of
writing such things are not newbies, they seldom need such tools any more.
For these functions to be usefull they must be standardized and
distributed with most distributions, so they are found by the newbies when
told by their peers to try them. And their peers need to know they are
there and work the same on most computers before they bother to tell the
newbies to try. When dealing with unexperienced users you don't want to
qualify every statement you make with things like "if that command is in
that directory, or you can see if you have this other command..."
I therefore urge people to try the commands below, install them in a file
in /etc/profiles.d, and report bugs and critisize all aspects. Then, when
they are as good as they can be, let's try to persuade the maintainers of
the various distributions to include them. When one day they are
everywhere, the old way will look rather backward.
The only thing I have not included here is a function to save the modified
path in the user's .bashrc. That step requires quite a bit of thinking
before we get it right. We need to have modalities for saving PATHs that
will be used by the system on the next boot, e.g, in /etc/rc on Fedora
systems, or in a file in /etc/sysconfig, where it can be sourced in by the
scripts that need it. On many systems, the display manager is started
directly from init. There needs to be a place to set a PATH so it will be
seen by the window manager. On some system this is simply the user's
.bashrc, because the display manager runs a bash script after setting its
user id to the new user, and everything else starts from there. But the
hardest part is, if setting the PATH systemwide becomes easier, it also
means more people will hose their compupters to the point where they
believe they can only get over it by reinstalling the whole system. That
too is little friendly. So, what can be done to make the whole thing
sufficiently intuitive?
Anyway, here comes the script:
------8<-----------------
#!/bin/bash
# Routines to manipulate the PATH:
#
# `path' prints one item per line.
# `path args' prepends args, then prints
# `path --quiet args' prepends args, doesn't print (for .profile)
# `path --append args' appends args, then prints
# `path --delete args' deletes args, then prints
#
# All the above clean up PATH removing duplicates and adding implicit dots.
# If you appended, but had it before, the earlier occurrence stays.
# Of course you can combine --quiet with --append or --delete.
#
# `path --show' prints one item per line with no cleanup
# These routines rely solely on bash built-in functionality.
# No external tools like sed, awk, expr, etc are called.
# They do not even depend on the PATH! (Good when the path has been hosed.)
# The routines are safe against spaces in the directory names.
# You can for instance have "/home/robert/My Programs" in your path
# You must, however, be careful to quote spaces on the command line.
# The only "problem" I know about is with directories whose names end
# in newlines :)
# More goodies:
# All options have long and short forms. See the code below for the
# complete table.
#
# `path -t args' Tests if the named args are all in the path.
# No output, only exit status
# `path -l args' Appends args _after_ having deleted them from the
# path i.e, moves them last or appends.
# 'path -L args' Moves last as above, but only args that are already
# in the path. I.e., moves last if present, no
appending
# new dirs.
####
# Auxiliary functions:
# Convert eg., "foo::bar:" to ":foo:.:bar:.:". Notice extra leading
# and trailing ':'.
__path_colon_dotify() {
local pth;
pth=:$1${1:+:} # If $1 is empty, we want ":", not "::".
pth=${pth//::/:.:} # Repeat becuse "::::" yields ":.::.:"
echo "${pth//::/:.:}"
}
# Convert eg., ":foo:bar:" to "foo:bar"
__path_uncolonify() {
local pth;
pth=${1#:}
echo "${pth%:}"
}
# Remove all but first occurrence of each element. Assume colonified
__path_simplify() {
local array pth dir n
IFS=: eval "array=(\$1)" # Yields an empty first element
pth=: # Make sure there is a trailing ':'.
n=${#array[*]} # Work backwards from the last element
while [ $n -gt 1 ]; do # Exclude empty leading element.
dir="${array[--n]}"
pth=":$dir${pth//:$dir:/:}"
done
echo "$pth"
}
# Tattarata, now comes...
# Add or remove args to/from PATH. Cleanup repetitions.
path() {
local quiet mode pth dir newline
mode=prepend
while [ $# -gt 0 ]; do
case $1 in
(- | --) shift; break;;
(-a | --append) mode=append;;
(-p | --prepend) mode=prepend;;
(-d | --delete) mode=delete;;
(-l | --last) mode=last;;
(-L | --last-if-present) mode=last-if-present;;
(-t | --test) mode=test;;
(-s | --show) mode=show;;
(-q | --quiet | --silent) quiet=true;;
(*) break;;
esac
shift
done
pth=$(__path_colon_dotify "$PATH")
case $mode in
prepend)
[ -n "$*" ] && IFS=: eval "pth=:\$*\$pth"
pth=$(__path_simplify "$pth")
PATH=$(__path_uncolonify "$pth");;
append)
[ -n "$*" ] && IFS=: eval "pth=\$pth\$*:"
pth=$(__path_simplify "$pth")
PATH=$(__path_uncolonify "$pth");;
delete)
pth=$(__path_simplify "$pth")
for dir; do
pth=${pth//:$dir:/:}
done
PATH=$(__path_uncolonify "$pth");;
last)
path -q -d "$@"
path -q -a "$@";;
last-if-present)
for dir; do
path -t "$dir" && path -q -l "$dir"
done;;
test)
for dir; do
case $pth in
(*:$dir:*) continue ;;
(*) return 1;;
esac
done
return 0;;
esac
if [ -z "$quiet" ]; then
newline=$'\n' # Using $'\n' directly in the substit. does not work
echo "${PATH//:/$newline}"
fi
}
- Previous message: kermit: "Re: Kernel Install Question: Is "make install" really necessary ?"
- Next in thread: aaron.toponce_at_gmail.com: "Re: Shell functions to manipulate the PATH"
- Reply: aaron.toponce_at_gmail.com: "Re: Shell functions to manipulate the PATH"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|