bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: PATH and $0


From: Stephane Chazelas
Subject: Re: PATH and $0
Date: Wed, 2 Aug 2006 13:51:15 +0100
User-agent: mutt-ng/devel-r562 (Linux)

On Thu, Jul 13, 2006 at 11:53:00PM -0600, Bob Proulx wrote:
> Stephane Chazelas wrote:
> > $0 will always  contain the file path, unless the script was
> > started as:
> >
> > bash script.sh
> >
> > And there's no script.sh in the current directory (in which case
> > sh/bash will have looked up script.sh in $PATH).
> 
> Of course using command as you have done will work for bash.


"command" is a POSIX command. So will work with every POSIX and
therefore every Unix conformant sh. Note that on some systems,
the Unix conformant sh is not in /bin, you may need to adapt the
she-bang line (note that the she-bang line is a non-standard Unix
feature).

> But I
> always feel better about using portable shell as much as possible.
> here is one way.
> 
>   pathfind() {
>     OLDIFS="$IFS"
>     IFS=:
>     for p in $PATH; do
>       if [ -x "$p/$*" ]; then
>         IFS="$OLDIFS"
>         echo "$p/$*"
>       fi
>     done
>     IFS="$OLDIFS"
>   }

On the contrary the code above has many problems:

1- If IFS was unset before, it becomes set to the empty string
after which has a different meaning.

2- word splitting when IFS contains non-blank characters varies
from one shell to the next, and in most implementations
(including bash and AT&T ksh, it differs from the way the shell
splits $PATH internally to look up a command ("/bin:" is "/bin"
and "" as far as PATH look up is concerned while bash world
splitting splits "/bin:" into only "/bin").

3- you forgot to disable filename generation, which means
wildcards will be expanded in "for p in $PATH"

4- there's a "break" (or return 0) missing, a "return 1" missing
if there's no match.

5- echo is not a portable command, use printf instead as POSIX
suggests.

> 
> > #! /bin/sh -
> > dir=$(
> >   cmd=$0
> >   [ -e "$cmd" ] || cmd=$(command -v -- "$cmd") || exit
> >   dir=$(dirname -- "$cmd")
> >   cd -P -- "$dir" && pwd -P
> > ) || exit
> > # untested
> >
> > should give you the absolute path of the directory portion of
> > the script path (unless that directory ends in newline
> > characters).
> 
> One thing to note about this script is that it canonicalizes path with
> respect to symlinks.  You do say that but without much
> fanfare.

That was intended. You can probably find as many caveats with
one approach or the other.

Note that if the binary file is a symlink itself, it doesn't
canonalise it.

[...]
> I think it is best to accept whatever path the user has provided
> verbatim.  I would not try to canonicalize the path in any way.  If
> the user has provided a path with /foo/bar/../../zoo/zam for example
> then I would just go with it because that path may actually be
> necessary just like that for some reason as yet unknown at this moment
> to us writing the code to be used in the future.  The reason I think
> this way is that I have tried to do things like canonicalizing
> previously myself and gotten burned by it.  I have learned it is
> better to avoid doing such things.

Then:

dir=$(
  cmd=$0
  [ -e "$cmd" ] || cmd=$(command -v -- "$cmd") || exit
  dir=$(dirname -- "$cmd")
  case $dir in
    /*) ;;
    *) dir=$(pwd -P)/$dir || exit;;
  esac
  printf '%s\n' "$dir"
) || exit

-- 
Stephane




reply via email to

[Prev in Thread] Current Thread [Next in Thread]