Skip to content(if available)orjump to list(if available)

Path Isn't Real on Linux

Path Isn't Real on Linux

30 comments

·April 29, 2025

mzajc

Fun fact: if you've ever had bash (or another shell) complain that a file doesn't exist, even though it's on $PATH, check if it's been cached by `hash`. If the file is moved elsewhere on $PATH and bash has the old path cached, you will get an ENOENT. The entire cache can be invalidated with `hash -r`.

noman-land

Wtf. TIL about hash.

JohnMakin

you just solved a bug I couldnt explain like 6 years ago

blcknight

Path globbing, pipes, redirection, job control (fg/bg), and all shell variables -- not just $PATH -- are all handled by the shell.

The kernel has no idea what the current process' environment $PATH is, and doesn't even parse any process environment variables at all.

thayne

PATH isn't just handled by the shell though. Many (but not all!) of the exec* family of functions in libc respect PATH.

kccqzy

There is also paths.h usually located at /usr/include/paths.h. It contains the default PATH macro _PATH_DEFPATH.

opello

It seems too far to go to say that because a system library holds some implementation details that the responsibility doesn't lie with the program using them. There's all sorts of complex interdependent details that make those kind of boundary distinctions difficult in many operating systems.

wpollock

Why would strace cat be useful here? By the time cat runs, it was obviously already found.

It is basic knowledge that PATH is used by a command interpreter to locate the pathname of binaries. This is true for Window's cmd.exe as well. I never heard of a system where locating files for execution was performed by a kernel.

Tsiklon

Silly tangentially related question; I like to think of myself as fairly competent in the Linux and unix world.

In the unix systems of the past was it easier to hold a more complete understanding of the system and its components in your head?

inlets

Why would the author think that the PATH environment variable is being used by the kernel? What an odd assumption.

MisterTea

Ignorance leading to assumptions. Their eureka moment: "The shell, not the Linux kernel, is responsible for searching for executables in PATH!" makes it obvious they haven't read up on operating systems. Shame because you should know how the machine works to understand what is happening in your computer. I always recommend reading Operating Systems: Three Easy Pieces. https://pages.cs.wisc.edu/~remzi/OSTEP/

quotemstr

The thing is, though, that PATH being a userspace concept is a contingent detail, an accident of history, not something inherent to the concept of an operating system. You can imagine a kernel that does path searches. Why not?

There's a difference between something being a certain way because it has to be that way in order to implement the semantics of the system (e.g. interrupt handlers being a privilege transition) and something being a certain way as a result of an arbitrary implementation choice.

OSes differ on these implementation choices all the time. For example,

* in Linux, the kernel is responsible for accepting a list of execve(2) argument-words and passing them to the exec-ed process with word boundaries intact. On Windows, the kernel passes a single string instead and programs chop that string up into argument words in userspace, in libc

* in Linux, the kernel provides a 32-bit system call API for 32-bit programs running on 64-bit kernels; on Windows, the kernel provides only a 64-bit system call API and it's a userspace program that does long-mode switching and system call argument translation

* on Windows, window handles (HWNDs, via user32.dll) in IPC message passing (ALPC, in ntoskrnl) are implemented in the kernel, whereas the corresponding concepts on most Linux systems are pure user-space constructs

And that's not even getting into weirder OSes! Someone familiar with operating systems in general can nevertheless be surprised at how a particular OS chooses to implement this or that feature.

LegionMammal978

One thing I was surprised to learn a couple years ago is that users and groups aren't really tracked much by the Linux kernel: they're just numeric IDs that track process and file ownership. So if you setuid() to a user ID that doesn't exist in /etc/passwd or anywhere else, the kernel won't stop you.

latchkey

If I have a file on machineA with uid10001 and I copy the file to machineB, I might want it to retain that uid, but it shouldn't matter to machineB that it doesn't map to a real user.

MrDarcy

You’ll see this observation all the time building containers.

mynegation

You and I and bunch of other people know it and take it to be self-evident, but someone discovered it (maybe recently, maybe they have known it for a while) and did the nice write up for people who had not have known that yet. https://xkcd.com/1053/

opello

The lucky 10,000 is a positive take on the situation. But the article using "real," which I think would connote to "legitimate" to most, seems a little more polarizing that sharing a discovery.

mynegation

Click bait title for sure

quotemstr

Well, execve(2) and execvp(3) are both "system" functions. C (which is already black magic for some people) invokes both by calling into functions exported from libc. If you're not super dorky^Wfamiliar with low-level systems stuff, you might guess that the two functions are implemented in the same place and in the same way. That the latter is just a libc wrapper around the former that does a PATH search is arcane detail you don't have to care about 99% of the time.

It's hard to appreciate how the world looks before you learn a fact. You can't unsee things.

null

[deleted]

null

[deleted]

drougge

Using "#!sh" at the top of the file does work, but not predictably. It may execute sh in your current directory, which is what Linux does, but your shell may override that (zsh does if the first attempt fails). So it works, but not the way you want it to.

And I'm sure other kernels do other things too.

cryptonector

It's real, it's just implemented by the shell -- same as all Unix-like operating systems. Heck, same as Windows.

taraindara

This actually helps explain some behaviors I’ve encountered. It was never a serious issue, since the answer is to use a full path. But is slightly annoying none the less. Understanding helps a lot.

dfedbeef

It's real in GNU/Linux tho...

dfedbeef

legitimately, if you're interested try writing a shell, your own libc, an elf loader even. It's fun! C is good and cool!

bawolff

Doesn't that go without saying?

SuperNinKenDo

I was trying to understand what the lede was here, and it turns out the author assumed that PATH was something understood by the kernel, which is rather an odd assumption, but perhaps one that others make.

I did get one thing out of this though. I had honestly wondered for the longest time why we need to call env to get the same functionality as PATH in a shebang.

Ironically, thanks to either an article I read here (or on the crustacean site) recently, I already knew that the shebang is something which is parsed by the kernel, but had not put two and two together at all.

Much like the author. So goes to show the benefits of exploring and thinking about seemingly "obvious" concepts.

khrbtxyz

Another bit of trivia about the shebang support in Linux is that is possible to build the kernel without it. https://github.com/torvalds/linux/blob/master/fs/Kconfig.bin...

  config BINFMT_SCRIPT
  tristate "Kernel support for scripts starting with #!"
  default y
  help
    Say Y here if you want to execute interpreted scripts starting with
    #! followed by the path to an interpreter.

semiquaver

[flagged]