5

I am writing a simple shell program. When I use /usr/bin/which with my shell in mac osx and ubuntu, it acts normally. When I use the exact same command on Red Hat Enterprise Linux Client release 6.3 (Santiago), I get this error: "which: no ANYCOMMANDHERE in ((null))".

Is there any intuition for this? I can't even find what the error means (Let me know if showing my source will help).

EDIT: My path is (from inside the shell):

$ echo $PATH
/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin

Thanks, Jon

7
  • 2
    What does echo $PATH say? It sounds like you have no PATH environment variable defined.
    – cdhowie
    Commented Oct 8, 2012 at 21:19
  • It sounds like a $PATH problem.
    – Blender
    Commented Oct 8, 2012 at 21:19
  • echo $PATH /usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin
    – Jonathan Friedman
    Commented Oct 8, 2012 at 21:21
  • Why are you using which in a shell script anyhow? It's bad practice to do so; if you really to know where things are, use the shell builtin type, but if you just want to start them, just call your commands without qualifying and let the shell find them. There's no reason to call $FOO when you can just call foo. Commented Oct 8, 2012 at 21:23
  • Charles Duffy. I'm writing a shell. Not a bash program.
    – Jonathan Friedman
    Commented Oct 8, 2012 at 21:25

5 Answers 5

5

The string (null) is substituted by some C libraries for arguments to printf's %s (string) conversion specifier, when the corresponding argument is a NULL pointer, e.g. for

 char *path = 0; /* This would normally be = getenv("PATH"). */
 printf ("which: no foobar in (%s)\n", path);

It appears that either your PATH environment variable is unset, or not exported. In these cases getenv("PATH") returns 0. As a last possibility, your which utility might have a mighty bug).

Does it work properly if you say export PATH?

8
  • Do you think it matters if I'm using /bin/tcsh (just realized that is the default on the server)? (Export isn't built in to tcsh I think (or my shell))
    – Jonathan Friedman
    Commented Oct 8, 2012 at 21:32
  • A csh variant? Ugh :-) Then you should say setenv PATH=...your path here...
    – Jens
    Commented Oct 8, 2012 at 21:40
  • But wouldn't it not matter since which is getting called from my shell? And if it does, how can I make this portable? Also, I dropped into /bin/sh, and ran my shell from there, and it didn't work.
    – Jonathan Friedman
    Commented Oct 8, 2012 at 21:41
  • It matters because you want to make sure your PATH is exported to the environment. The Bourne Shells do this with export, the csh lineage with setenv. We want to make sure PATH is set, so we can find if there's a problem in the which utility or somewhere else.
    – Jens
    Commented Oct 8, 2012 at 21:43
  • $ echo $PATH /usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin. My bad.. I posted this in an edit.
    – Jonathan Friedman
    Commented Oct 8, 2012 at 22:02
3

Actually, what all other people here already answered - PATH not being set, is true in one way, but false in another way.

I had the same problem (using bash). It seems, a bash "feature" turns into a miss-feature: Bash creates a shell variable (not environment variable!) PATH which it then uses for lookups.

So, the situation is: There exists a shell variable called PATH. But it's not an environment variable. This means, it's not exported to newly created child processes. One thing, we should learn from this, is that using echo $PATH is no way to figure out whether the environment variable PATH is set. Same prolem with set | grep "^PATH=" However, one way to do so is by using the external command env: env | grep "^PATH="

Example shell session:

$ env -i bash
# A new shell is started with empty environment.
# What follows happens in this new shell:
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
$ set | grep "^PATH="
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
$ env | grep "^PATH="
$ which ls
which: no ls in ((null))
$ export PATH
$ env | grep "^PATH="
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
$ which ls
/bin/ls

Depending on what you're trying to achieve, you can ask the shell to act like a login shell, for bash it's -l. With that, /etc/profile and other files are sources which set up PATH properly by default:

$ env -i bash -l
$ which ls
/bin/ls

The alternative, is to export PATH, see above.

1
  • Ran into this issue installing txt2man, and this resolved it. Much appreciated!
    – DMor
    Commented Dec 30, 2017 at 7:32
1

I can reproduce the same output from tcsh:

env -u PATH which ls
which: no ls in ((null))

In csh and tcsh, there's a distinction between environment variables (which are common to all processes) and shell variables (which are local to the current invocation of the shell). It uses the same syntax to refer to both, e.g., $PATH. If you have a shell variable and an environment variable of the same name, then $PATH refers to the shell variable.

[t]csh uses different syntax to set shell vs. environment variables:

set shell_var = foo
setenv env_var bar

(Bourne-based shells such as bash have a different syntax and a different terminology; an environment variable is a shell variable that's been "exported", or that's inherited from the calling process.)

Based on the symptoms you describe, you have a shell variable $PATH (which is useless), but no environment variable of the same name. That shouldn't normally happen. Check your .cshrc, .tcshrc, and/or .login files for statements that set $PATH.

You should be able to work around the immediate problem like this:

setenv PATH "$PATH"   # set the environment variable
unset PATH            # unset the shell variable, just to avoid confusion

(Don't do the unset PATH until you've confirmed that things are working correctly.)

Just to add to the frivolity, [t]csh has a special shell variable $path (note lowercase); its value is an array consisting of the :-separated components of the $PATH environment variable. Setting either will automatically update the other:

 setenv PATH /usr/bin:/bin # sets $path to ( /usr/bin /bin )
 set path = ( /usr/local/bin $path ) # sets $PATH to '/usr/local/bin:/usr/bin:/bin'

This can be convenient, but if you prefer you can just ignore $path and deal with $PATH. Just make sure that you're setting the environment variable $PATH (using setenv), not the useless shell variable of the same name.

2
  • Thanks for the info! I think it might have to do with default env var passing to children with fork. I'm not 100% though.. still testing. Commented Oct 9, 2012 at 1:22
  • Children created by fork() inherit the $PATH environment variable of their parents. Every process should have a default $PATH. If $PATH is unset for a given process, something must have unset it explicitly. Commented Oct 9, 2012 at 4:38
0

It sounds like your PATH environment variable is the problem. For instance, if you run which asdf normally you will get something like:

which: no asdf in (/usr/local/bin:/usr/bin:...)

So it appears your PATH environment variable is something like (NULL). The other possibility is that there's something wrong with the which binary on your RHEL box.

0

So it turns out the issue is specific to redhat and the forking mechanism. Child processes are not given the default env vars while in ubuntu and mac osx they are. This means that I had to explicitly set the envp to the parent's (in a call to execve).

Apologies for the misleading phrasing in my question (although it is hard to pinpoint these type of things).

Thanks for all the help! Jon

3
  • Er. This makes no sense. How were you calling fork/exec before?
    – Random832
    Commented Oct 9, 2012 at 5:16
  • I wasn't explicitly passing the environment variables to the child. There apparently is a difference between the different linux flavors for this behavior. Commented Oct 9, 2012 at 19:26
  • My point was, what exactly did your call to exec look like? You can't just leave them out if you call execve/execle, and if that worked it only did so by accident - you have to call execv/execl to get it to implicitly pass the parent's environment. I'm asking this because there's not actually a difference, or any sane reason for there to be a difference, if you did it right. It's also possible that none of them were getting the parent's environment, and the implementation of /usr/bin/which (which is not a standard tool) was doing something different.
    – Random832
    Commented Oct 9, 2012 at 22:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .