This works

sh /opt/gap-4.11.1/bin/gap.sh

This does not work

sh gap.sh

and returns error message

sh: 0: Can't open gap.sh

But the required directory is in $PATH, as confirmed with echo $PATH


What's wrong?


2 Answers 2


While POSIX allows sh implementations to look file up in $PATH upon sh file when file is not found in the current working directory, as that's what ksh (on a subset of which the sh specification is based) does, it doesn't require it and most sh implementations nowadays don't (even mksh doesn't) as that's not desirable.

The fact that POSIX allows it and that some implementations do actually causes the reverse problem:

sh -- "$file"

Is not guaranteed by POSIX to interpret the file whose path is stored in $file, so you have to do:

case $file in
  ('') echo >&2 error;;
  (*/*) sh -- "$file";;
  (*) sh "./$file";;

portably to work around it.

That's also why you see people use:

sh ./file

When they want to make sure it's the file in the current directory that is interpreted and not run the risk of interpreting some completely unrelated script in $PATH if file happens to be missing, just like you do ./ls to run the ls command in the current working directory instead of the one in $PATH.

There is a similar problem with the . utility which does a $PATH lookup when $file doesn't contain a /. While POSIX doesn't allow falling back to interpreting the file in the current directory if it can't be found in $PATH, bash or zsh when not in sh mode do. csh' equivalent to .: source does not do a $PATH lookup. In zsh, source falls back to a $PATH lookup. In bash, source behaves like ..

Here, as your script is in $PATH, and assuming you have execute permission for it, you can just enter:


For gap.sh to be looked up in $PATH. If the script has no shebang, it should be interpreted by a POSIX sh compliant interpreter.

If you have read but not execute permission, you can do:

sh -c '. gap.sh' sh arguments for gap.sh

Which as seen above will do a $PATH lookup.

As a history note, upon ksh file, ksh used to do the $PATH lookup first. That was a security vulnerability on systems that supported setuid scripts as you could do:

ln -s /path/to/some/setuid/ksh/script /tmp/file
echo sh > ~/bin/file
cd /tmp

And invoke exec("file", ...) which with a #! /bin/ksh - shebang would become /bin/ksh - file run as the setuid user and ksh would end up interpreting ~/bin/file instead of /tmp/file, so you'd get a root shell if the script was setuid root.


PATH will be taken into account for the program being executed, in this case sh.

As /bin:/opt/gap-4.11.1/bin is in your PATH, you should be able to run it "directly", running:


as long as it has execute permission.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.