[comp.unix.questions] Which script

bernie@DIALix.oz.au (Bernd Felsche) (09/09/90)

In article <1990Sep7.152354.9439@ecn.purdue.edu> patkar@helium.ecn.purdue.edu (The Silent Dodo) writes:
>I have a question about shell scripts.  How can a shell script
>(sh or csh) find out its own file name?  Actually, I need to
>know only the directory in which it resides.
>
The trivial solution is that the program name is in $0.

Since you want the path (directory), it is only partially useful,
because the program name is what you actually typed.  So if you
typed 'freddo', then $0 becomes that... no path.

The solution is to parse the current PATH if the program name does
not begin with a '/'. On a BSD machine, you can use the 'which'
command (come Sys V have it also), or you can work it out by:

:
# bourne shell script template
#

prog=$0
case $prog in
	/*)	echo $i
		break
		;;
	*)
		OIFS="$IFS"
		IFS=":$IFS"

		for i in $PATH
		do
			if [ -x $i/$prog ]
			then
				break
			fi
		done
		echo $i/$prog
		IFS="$OIFS"
		;;
esac

Instead of the echo's, you can use a fullname= etc to find what you
need.  To get the directory name is left as an exercise :-)

bernie

p.s.  Send no cash.  Just Bullion.

rsl@cbnewsm.att.com (randolph.little) (09/11/90)

In article <563@DIALix.UUCP>, bernie@DIALix.oz.au (Bernd Felsche) writes:
> In article <1990Sep7.152354.9439@ecn.purdue.edu> patkar@helium.ecn.purdue.edu (The Silent Dodo) writes:
> >I have a question about shell scripts.  How can a shell script
> >(sh or csh) find out its own file name?
> 
> The solution is to parse the current PATH if the program name does
> not begin with a '/'. On a BSD machine, you can use the 'which'
> command (come Sys V have it also), or you can work it out by:
> 
> :
> # bourne shell script template
> #
> 
> prog=$0
> case $prog in
> 	/*)	echo $i
#                    ^^^   Should be $0
> 		break
> 		;;
# Add test for explicit current directory search:
	./*)	echo `pwd`/`basename $0`
		break
		;;
> 	*)
> 		OIFS="$IFS"
> 		IFS=":$IFS"
> 
> 		for i in $PATH
> 		do
> 			if [ -x $i/$prog ]
> 			then
> 				break
> 			fi
> 		done
> 		echo $i/$prog
> 		IFS="$OIFS"
> 		;;
> esac
> 
> Instead of the echo's, you can use a fullname= etc to find what you
> need.  To get the directory name is left as an exercise :-)
> 
> bernie
> 
> p.s.  Send no cash.  Just Bullion.

The first change corrects a typo, where $i should be $0; while the
second change adds another pattern to handle the case where the command
is invoked in the working directory by typing ./command (which bypasses $PATH).

Randolph S. Little <randolph.little@att.com>

jimr@hp-lsd.COS.HP.COM (Jim Rogers) (09/13/90)

Following is a modest script in ksh to report the path of a script.
I suspect that this can be done much more elegantly.  The beauty of this
script is that it works correctly.


#! /bin/ksh

# This script displays the path name of the script itself
# from the command argument $0

name=$0
# look for the path in the above directory
first=${name#..}
if [ "$first" != "$name" ]
then
	cd ..
	fpth=$PWD$first
else
# look for the path in the current directory
	first=${name#.}
	if [ "$first" != "$name" ]
	then
		fpth=$PWD$first
	else
		case $name in
#	Absolute path specified
		/*)	fpath=$name; break;;
#	relative path below current directory
		*)	fpath=$PWD/$name; break;;
		esac
	fi
fi
spath=`print $ppth | tr "/" " "`	
for file in $spath
do
	continue
done
pth=${fpath%/$file}
print "path to $name is $pth"




Jim Rogers
Hewlett Packard Company

anw@maths.nott.ac.uk (Dr A. N. Walker) (09/13/90)

In article <563@DIALix.UUCP> bernie@DIALix.oz.au (Bernd Felsche) writes:
>In article <1990Sep7.152354.9439@ecn.purdue.edu> patkar@helium.ecn.purdue.edu
(The Silent Dodo) writes:
>>[...] How can a shell script
>>(sh or csh) find out [the directory of] its own file name?
> [gives shell script to parse the PATH]

	Just to point out that any such script is easily spoofed, in case
this is a security- or accounting-related problem.  Try something like

	$ PATH=/something/innocuous export PATH
	$ /bin/sh
	$ PATH=/secret/directory	# note, no export
	$ spoof

and "spoof" will look for itself in "/something/innocuous", even though it
was found in "/secret/directory".  At least, it does in SunOS 4.0.3, and it
does with our somewhat modded SysV shell, though I don't remember seeing
anywhere a definition of what *should* happen if an exported variable is
masked by an unexported one.

-- 
Andy Walker, Maths Dept., Nott'm Univ., UK.
anw@maths.nott.ac.uk

bernie@DIALix.UUCP (Bernd Felsche) (09/15/90)

In article <1990Sep10.185245.25531@cbnewsm.att.com> rsl@cbnewsm.att.com (randolph.little) writes:
>In article <563@DIALix.UUCP>, bernie@DIALix.oz.au (Bernd Felsche) writes:
>> The solution is to parse the current PATH if the program name does
>> not begin with a '/'. On a BSD machine, you can use the 'which'
>> command (come Sys V have it also), or you can work it out by:
>> 
[some script deleted]
>> prog=$0
>> case $prog in
>> 	/*)	echo $i
>#                    ^^^   Should be $0
>> 		break
>> 		;;
># Add test for explicit current directory search:
>	./*)	echo `pwd`/`basename $0`
                     ^^^^^^^^^^^^^^^^^
This will break if somebody uses ./bin/whodo or something.  Even
worse, you (and I) neglected to check for the existence and execute
permissions.

What about ../*  ??

After posting, I did notice the deficiency, (actually woke up in the
middle of the night!)  I trust that the end-use is not in a
life-threatening environment :-)

>		break
	I could use one...
[...rest of script deleted...]
>
>The first change corrects a typo, where $i should be $0; while the

Sprung!  I actually kludged my "which" script to include in the
message.

>second change adds another pattern to handle the case where the command
>is invoked in the working directory by typing ./command (which bypasses $PATH).
>
>Randolph S. Little <randolph.little@att.com>

Thank you for your vigilance.  Constructive criticism should be
rewarded.

So here's the which script, it can be easily tailored to suit the
original request... barring typos!

Some serious testing has gone into this, so please let me know if
I've missed something.

I don't want to put shell function (or korn shell aliases) into it.
If you're that way inclined, please post the results.
------------------------------- cut here -----------------------
:
# bourne shell which
#
# which what?  :-)
#
# By Bernd Felsche.  bernie@DIALix.oz.au
#
# bug with relative paths fixed Sat Sep 15 17:23:32 WST 1990
#

if [ $# = 0 ]
then
	echo "usage: $0 program [program...]" 1>&2
	exit 1
fi

for prog in $* ; do
	path="NO PATH AVAIL"
	case $prog in
		/*)	path=$i
			;;

		./*|../*)
			dir=`dirname $prog`
			if [ -d $dir ] ; then
				path=`(eval cd $dir ; pwd)`/`basename $prog`
			fi
			;;

		*)	OIFS="$IFS"
			IFS=":$IFS"
			for i in $PATH ; do
				if [ -f $i/$prog -a -x $i/$prog ] ; then
					path=$i/$prog
					break 		# for $PATH
				fi
			done
			IFS="$OIFS"
			;;
	esac

	if [ -x $path -a -f $path ] ; then
		echo $path
	else
		echo $prog not found 1>&2
		exit 2
	fi
done
------------------------- end -----------------------

bernie

P.S. bug reports & critiques welcome.

bernie@DIALix.UUCP (Bernd Felsche) (09/15/90)

In article <1990Sep13.151130.10215@maths.nott.ac.uk> anw@maths.nott.ac.uk (Dr A. N. Walker) writes:
>	Just to point out that any such script is easily spoofed, in case
>this is a security- or accounting-related problem.  Try something like
>
>	$ PATH=/something/innocuous export PATH
>	$ /bin/sh
>	$ PATH=/secret/directory	# note, no export
>	$ spoof
>
>and "spoof" will look for itself in "/something/innocuous", even though it
>was found in "/secret/directory".  At least, it does in SunOS 4.0.3, and it
>does with our somewhat modded SysV shell, though I don't remember seeing
>anywhere a definition of what *should* happen if an exported variable is
>masked by an unexported one.

IMHO: Your shell is broken.  Not my script.  On all the real bourne 
shells I've tested this on (two so far) the results are dependent on 
the _environment_ PATH setting.

Your shell is not using PATH as set in the environment, only its
internal working space value.

Perhaps somebody on the net can elucidate as to the divergence in
philosophy. (I get polysyllabic after 6 hours of reading news.)

bernie.

anw@maths.nott.ac.uk (Dr A. N. Walker) (09/19/90)

In article <572@DIALix.UUCP> bernie@DIALix.oz.au (Bernd Felsche) writes:
[re my comment that any script that determines how it was called is
easily spoofed]
>IMHO: Your shell is broken.  Not my script.  On all the real bourne
>shells I've tested this on (two so far) the results are dependent on
>the _environment_ PATH setting.
>
>Your shell is not using PATH as set in the environment, only its
>internal working space value.

	Well, our PDP 11 is now deceased, so I can't run a *real*
Bourne shell [the one in pseudo Algol] without compiling up the source,
but I think you have misunderstood "my" result.  Sorry if this was
caused by lack of clarity in the original;  let me try again.

	Suppose I am running a shell [A], and invoke a sub-shell [B].
In B, I set "PATH=/something/or/other", without exporting it.  This
PATH is now used to find commands.  I invoke a shell script "spoof".
This script is run with $0 set to "spoof", but it does *not* inherit
PATH from B.  Thus "spoof" is actually "/something/or/other/spoof",
but there is nothing in the environment *of "spoof"* that enables it
to recover this information.  In the shells instantly available to me,
PATH is in fact inherited from A, so anything that *"spoof"* does to
discover how it was called is likely to be wrong, depending on the
ingenuity of the PATH setting in A.

	By working slightly harder (eg, writing a C program), "spoof"
can be supplied with whatever $0 and PATH (or anything else in the
environment, such as IFS) a bad guy likes.  Thus, any shell script
that includes code like

		case $0 in
			foo)	some command
		esac

is insecure.  Of course, code that searches "$PATH" is perfectly OK
for run-of-the-mill utility scripts, where only the caller is hurt
if the script does something unexpected.

-- 
Andy Walker, Maths Dept., Nott'm Univ., UK.
anw@maths.nott.ac.uk

gt0178a@prism.gatech.EDU (BURNS) (09/19/90)

in article <571@DIALix.UUCP>, bernie@DIALix.UUCP (Bernd Felsche) says:

[supplies expanded which script]

I saved your script as I thought it was an interesting piece of work, but
I will point out a couple of problems:

1) The easy one was my (Dynix's) version of sh didn't like your syntax for
test(1), so I used ksh.

2) If you do (your script is called wh):

wh wall                 # you get:
wall not found

Similarly, w/ the system supplied which:

which wall              # you get:
no wall in . /hydra9/gt01/gt0178a /usr/local/bin /usr/ucb /bin /usr/bin /usr/att/bin /usr/att/usr/bin

which is of course my path. Finally, the system supplied (at least for me)
whereis gives:

ll `whereis wall`       # you get:
wall: not found
-rwxr-x---  1 root        20480 May 21  1988 /bin/wall*
-r--r--r--  1 root          984 May 21  1988 /usr/man/man1/wall.1

Ignore the 1st line, unless it is the only one. Obviously wall is failing
the executable test in the 1st 2 commands. Moral - don't stick to one tool
(there are lots of things whereis can't find).
-- 
BURNS,JIM
Georgia Institute of Technology, Box 30178, Atlanta Georgia, 30332
uucp:	  ...!{decvax,hplabs,ncar,purdue,rutgers}!gatech!prism!gt0178a
Internet: gt0178a@prism.gatech.edu

bernie@DIALix.UUCP (Bernd Felsche) (09/21/90)

Lesson 1: I can make misteaks too.

	The method I used to check for the PATH was to create a
	temporary file in the current directory, with a pleasing
	message inside.  I exported my current PATH, starting with
	/bin, and then prepended my normal path with ".",
	(it's usually at the end), and executed the command.

	The command name was that of a command in the /bin
	directory, with my own version in .  Suffice to say, I got
	the result expected from executing the /bin command.

	Misteak: The name of the command was test. This is of
	course, a builtin in modern shells, so it got executed
	without refernce to the PATH at all.

In article <13838@hydra.gatech.EDU> gt0178a@prism.gatech.EDU (BURNS) writes:
>in article <571@DIALix.UUCP>, bernie@DIALix.UUCP (Bernd Felsche) says:
>
>[supplies expanded which script]
>
>I saved your script as I thought it was an interesting piece of work, but
>I will point out a couple of problems:
				:-(
>
>1) The easy one was my (Dynix's) version of sh didn't like your syntax for
>test(1), so I used ksh.

Lesson 2: no two shells are alike, even if they are superficially
	the same.  I often find subtle differences between BSD, V.0, V.2,
	V.3 and XENIX /bin/sh.  Using ksh is a good idea, because it's 
	newer and less versions proliferate... at the moment.

	Perhaps you can tell me which test syntax Dynix is happy
	with. (Don't grumble. It takes practice to find prepositions
	to end a sentence with :-) )
>
>2) If you do (your script is called wh):
>
>wh wall                 # you get:
>wall not found
>
>Similarly, w/ the system supplied which:
>
>which wall              # you get:
>no wall in . /hydra9/gt01/gt0178a /usr/local/bin /usr/ucb /bin /usr/bin /usr/att/bin /usr/att/usr/bin
>
>which is of course my path. Finally, the system supplied (at least for me)
>whereis gives:
>
>ll `whereis wall`       # you get:
>wall: not found
>-rwxr-x---  1 root        20480 May 21  1988 /bin/wall*
 ^^^^^^^^^^
Does this mean that you wouldn't have been able to execute it
anyway?  If you type "wall", don't you get a permission denied?

>-r--r--r--  1 root          984 May 21  1988 /usr/man/man1/wall.1
>
>Ignore the 1st line, unless it is the only one. Obviously wall is failing
>the executable test in the 1st 2 commands. Moral - don't stick to one tool
>(there are lots of things whereis can't find).

The purpose of the which tool is to tell you _which_ command will be
executed.  It is designed to be quick. It does not attempt to
interpret functions, aliases or similar creatures.

The output of whereis above is useful only if you change your gid
or uid, after which (no pun), wh will find wall.

>-- 
>BURNS,JIM
	Thanks for the beta testing, Jim.  Production releases of "which" 
	(V12.09.06.20) will be shipped RSN, at a cost of US$825. :-)  
	Order now and avoid the rush.

bernie

p.s. Misteaks are deliberate. 

gt0178a@prism.gatech.EDU (BURNS,JIM) (09/22/90)

in article <574@DIALix.UUCP>, bernie@DIALix.UUCP (Bernd Felsche) says:

> 	Misteak: The name of the command was test. This is of
> 	course, a builtin in modern shells, so it got executed
> 	without refernce to the PATH at all.

He-he-he.

> 	Perhaps you can tell me which test syntax Dynix is happy
> 	with. (Don't grumble. It takes practice to find prepositions
> 	to end a sentence with :-) )

Basically, our sh seems to use /bin/test. You get the same errors in ksh
if you replace the []'s with /bin/test. It didn't like '-x'. I'll mail you
the relevant man pages from test(1) and ksh(1).

>>-rwxr-x---  1 root        20480 May 21  1988 /bin/wall*
>  ^^^^^^^^^^
> Does this mean that you wouldn't have been able to execute it
> anyway?  If you type "wall", don't you get a permission denied?

Yes.

> The purpose of the which tool is to tell you _which_ command will be
> executed.  It is designed to be quick. It does not attempt to
> interpret functions, aliases or similar creatures.

I'm using it to find out where an executable lives. Useful if your system
has several versions, and which you execute depends on how your PATH is
ordered. And since I *can't* execute wall(1) (or finger(1)) on that
machine, locating it tells me why - it's not that it's not there, just
that it's not world executable. By extension, I have an $ETC variable I
can also search for the occaisional command that's not normally in my
$PATH, like ping(1).
-- 
BURNS,JIM
Georgia Institute of Technology, Box 30178, Atlanta Georgia, 30332
uucp:	  ...!{decvax,hplabs,ncar,purdue,rutgers}!gatech!prism!gt0178a
Internet: gt0178a@prism.gatech.edu

meissner@osf.org (Michael Meissner) (09/23/90)

In article <13992@hydra.gatech.EDU> gt0178a@prism.gatech.EDU
(BURNS,JIM) writes:

| > The purpose of the which tool is to tell you _which_ command will be
| > executed.  It is designed to be quick. It does not attempt to
| > interpret functions, aliases or similar creatures.
| 
| I'm using it to find out where an executable lives. Useful if your system
| has several versions, and which you execute depends on how your PATH is
| ordered. And since I *can't* execute wall(1) (or finger(1)) on that
| machine, locating it tells me why - it's not that it's not there, just
| that it's not world executable. By extension, I have an $ETC variable I
| can also search for the occaisional command that's not normally in my
| $PATH, like ping(1).

I like bash's type -all feature, where you can find all occurances of
a command in the PATH.
--
Michael Meissner	email: meissner@osf.org		phone: 617-621-8861
Open Software Foundation, 11 Cambridge Center, Cambridge, MA, 02142

Do apple growers tell their kids money doesn't grow on bushes?

gwyn@smoke.BRL.MIL (Doug Gwyn) (09/23/90)

In article <MEISSNER.90Sep22142134@osf.osf.org>, meissner@osf.org (Michael Meissner) writes:
> I like bash's type -all feature, where you can find all occurances of
> a command in the PATH.

Or, assuming PATH is exported, you can use a shell script like this:

#!/usr/5bin/sh
#	which, every -- which cmd in PATH is executed
#	adapted from Kernighan & Pike

#	last edit:	85/03/07	D A Gwyn
#	SCCS ID:	@(#)which.sh	1.4

opath=$PATH
PATH=/usr/5bin:/bin:/usr/bin

name=`basename $0`			# "which" or "every"

if [ $# -eq 0 ]
then	echo Usage: $name 'command(s)' 1>&2
	exit 2
fi

prefixes=`echo $opath | sed 's/^:/.:/
			     s/::/:.:/g
			     s/:$/:./
			     s/:/ /g'`

ex=1					# assume nothing found
for cmd in $*
do	nf=1				# assume cmd not found
	case $cmd in
	/*)	if [ -f $cmd -a -x $cmd ]
		then	echo $cmd
			nf=0		# found it
		fi
		;;
	*)	for pfx in $prefixes
		do	if [ -f $pfx/$cmd -a -x $pfx/$cmd ]
			then	echo $pfx/$cmd
				nf=0	# found it
				if [ $name = which ]
				then	break
				fi
			fi
		done
		;;
	esac
	if [ $nf -ne 0 ]
	then	echo $name: $cmd: 'not found' 1>&2
	else	ex=0			# found one
	fi
done

exit $ex

chet@cwns1.CWRU.EDU (Chet Ramey) (09/25/90)

In article <MEISSNER.90Sep22142134@osf.osf.org> meissner@osf.org (Michael Meissner) writes:

>I like bash's type -all feature, where you can find all occurances of
>a command in the PATH.

Bash's `type' command is very nice.  I have written shell functions
implementing ksh `whence', csh `which', 10th edition sh `whatis', and
regular bsh `type' using it, so everything else that's out there in
wide use can be implemented using it.

Chet
-- 
Chet Ramey				``Levi Stubbs' tears run down
Network Services Group			  his face...''
Case Western Reserve University	
chet@ins.CWRU.Edu