Multiple executables in path

Paul Falstad pfalstad at phoenix.Princeton.EDU
Fri Jan 25 04:18:34 AEST 1991


brnstnd at kramden.acf.nyu.edu (Dan Bernstein) wrote:

>Same as the first one, but converting . in PATH into the actual cwd:
>
>  setenv WD "`pwd`"
>  echo `echo "$PATH" | tr : '\012' | sed -e 's:^\.$:'"$WD": -e 's:$:/!:1': -e 's:^:/.[.]:'`\
>    | sed 's:/\.\./:/:g'

This is not really a change in optional behavior, it is a bug fix.  Your
which should act the right way if you have . in your path.

>Original:
>
>  echo `echo "$PATH" | tr : '\012' | sed -e 's:$:/!:1': -e 's:^:/.[.]:'`\
>    | sed 's:/\.\./:/:g'

>The same thing, but removing the *:
>
>  ls -dFL `echo "$PATH" | tr : '\012' | sed -e 's:$:/!:1': -e 's:^:/.[.]:'`\
>    | sed -n -e 's:/\.\./:/:g' -e 's/*$//p'

(I could point out that if you have files ending in * in your path, this
acts weird, but I won't.  For the most part it works fine, I'm sure.
I was wrong to imply that this was impossible with standard tools.)

This is kind of confusing.  In order to print only executables, you have
changed the echo to an ls -dFL and added another expression to the
second sed.  It is not at all intuitive what the difference between
these two versions are.  I suppose someone smart enough to deduce what
your original did could figure out what the second one did, but it's not
obvious.  However, with Tom's, the difference is:

< printf "$path\n" if -x ($path="$dir/$file");
---
> printf "$dir/$file\n";

The difference is obvious; you've just removed the test.  With Tom's, to
remove the checking to see if the file is executable or not, you just remove
the test.  In yours, to put in the checking, you change 'echo' to 'ls
-dFL' and add another expression to your sed command.  This is not
obvious.

>The same thing, but showing only the first executable:
>
>  ls -dFL `echo "$PATH" | tr : '\012' | sed -e 's:$:/!:1': -e 's:^:/.[.]:'`\
>    | sed -n -e 's:/\.\./:/:g' -e 's/*$//p' | head -1

This is a bit inefficient; all you've done here is add one more process
to filter the output of the original.  I doubt anyone would choose this
method as a standard implementation for which.

>You can easily combine these to make other behaviors if you want, e.g.,
>to just show the first executable including in ., or whatever.

Certainly not as easily as changing the perl script.  These are shell
hacks, not programs.


The reason I call them hacks, and part of the reason they're confusing
is that they depend on a bug in csh for their operation!  Let's say we
try to figure out how the original (buggy) version works:

>  echo `echo "$PATH" | tr : '\012' | sed -e 's:$:/!:1': -e 's:^:/.[.]:'`\
>    | sed 's:/\.\./:/:g'

I know as a shell writer I should know at first glance what this does,
but I must confess I had to break it down.  Let's see:

1 phoenix:~/News echo "$PATH" | tr : '\012' | sed -e 's:$:/ls': -e 's:^:/.[.]:'

/.[.]/n/uffda/b/pfalstad/scr/ls
/.[.]/n/uffda/b/pfalstad/bin/sun4/ls
/.[.]/usr/princeton/bin/ls
/.[.]/usr/ucb/ls
/.[.]/usr/bin/ls
/.[.]/bin/ls
/.[.]/u/maruchck/scr/ls
/.[.]/u/cs320/bin/ls
/.[.]/u/subbarao/bin/ls
/.[.]/u/maruchck/bin/ls
/.[.]/usr/hosts/ls
/.[.]/usr/princeton/bin/X11/ls
/.[.]/usr/etc/ls
/.[.]/etc/ls
3 phoenix:~/News echo `echo /.[.]/etc/ls`
echo: No match.

4 phoenix:~/News echo `echo '/.[.]/etc/ls'`

5 phoenix:~/News exit

What's going on?  It turns out that your version depends on a bug in
csh, or at least a stupid feature.  I don't think that the output of
`...` substitution should be globbed at all (It's not obvious that it
should be), and if it is, and there is no match, it should print an
error message.  It's this same 'feature' that requires you to put 'set
noglob' and 'unset noglob' around eval `tset -s` in your .login.  This
bug has been fixed in some versions, including itcsh.  The only reason I
could figure out how your alias works is because I've taken a lot of
time studying the standard shells, and have read the csh bug list.
Writing a shell didn't hurt either.  I would never write a script
that used a csh bug for part of its operation and then post it to the
net as an example.

>If you want to put these in a script instead: Replace !:1 with '"$1"',
>and top the script with #!/bin/csh -f.
>
>If you want to accept aliases from .cshrc: Same, but remove the -f.

What do you mean?  You mean, change your implementation so it uses
aliases from .cshrc?  I don't see the difference this makes.

>> (That's only a suggestion.  He probably does like which to have that behavior.)
>
>Obviously.

Wasn't obvious from your article.  You should have fixed your solution
first, and then explained why it's bad to have . in your path.  You
sounded like a lazy programmer trying to get out of making a bug fix.  I
know the feeling. :-)

>> But Tom's solution can easily be changed to have either behavior.
>
>So what? So can any reasonable solution.

You're not really changing the solution, you're just filtering the
output.  Tom's can be filtered as well.

If you want to write a nice version of which that uses standard tools (which
I have no qualms about, I'm no perl groupie), you should use a loop.
It can be done in csh; SunOS /usr/ucb/which is a csh script.  Better
yet, use a decent shell.  If you like your version because it's fast and
it works the way you want it to, that's fine.  But you must admit, it's
hardly an example of good code.  I can't see how any csh hack that can't
easily be translated into a decent shell like sh or ksh can be called
good code.

--
Paul Falstad, pfalstad at phoenix.princeton.edu PLink:HYPNOS GEnie:P.FALSTAD
In the heat of composition I find that I have inadvertently allowed
myself to assume the form of a large centipede.  I am accordingly
dictating the rest to my secretary.
409 shift/reduce, 62 reduce/reduce conflicts.  Beat that!



More information about the Alt.sources.d mailing list