Rollup merge of #153469 - Albab-Hasan:doc/command-path-search-behavior, r=ChrisDenton

docs: clarify path search behavior in std::process::Command::new

the existing docs mentioned that `PATH` is searched in an "os defined way" and that it could be controlled by setting PATH on the command but never explained which `PATH` is actually used.

on unix the key thing to understand is that when you modify the childs environment (via `env()`, `env_remove()`, or `env_clear()`), the `PATH` search uses whatever `PATH` ends up in the child's environment not the parents. so if you call `env_clear()` and forget to set `PATH`, you don't get the parents `PATH` as a fallback; you get the OS default (typically `/bin:/usr/bin`) which often won't find what you need.

the three cases are now documented:
- unmodified env: child inherits parents `PATH`, that gets searched
- `PATH` explicitly set `via env()`: the new value is searched
- `PATH` removed (`env_clear` or `env_remove`): falls back to OS default, not the parents `PATH`

on windows rust resolves the executable before spawning rather than letting `CreateProcessW` do it. the search order is: childs `PATH` (if explicitly set) first then system-defined directories then the parents `PATH`. the existing note about issue rust-lang/rust#37519 is preserved.

limitations in this doc:
- the unix fallback path behavior ("typically `/bin:/usr/bin`") is verified for linux/glibc. behavior on macOS, BSD, and musl is similar in practice but not fully confirmed here.
- the windows search order is summarized as "system-defined directories" which actually includes the application directory (the directory of the calling process's executable) as a distinct step before the system dirs that detail is omitted for brevity.
- `posix_spawnp` `PATH` source (calling process environ vs envp argument) is ambiguous in the `POSIX` spec; the behavior here is inferred from the guard in `unix.rs` that bypasses `posix_spawn` when `PATH` has been modified.

closes rust-lang/rust#137286 (hopefully)
This commit is contained in:
Jacob Pratt
2026-04-14 23:02:32 -04:00
committed by GitHub
+41 -12
View File
@@ -617,21 +617,50 @@ impl Command {
/// Builder methods are provided to change these defaults and
/// otherwise configure the process.
///
/// If `program` is not an absolute path, the `PATH` will be searched in
/// an OS-defined way.
///
/// The search path to be used may be controlled by setting the
/// `PATH` environment variable on the Command,
/// but this has some implementation limitations on Windows
/// (see issue #37519).
/// If `program` is not an absolute path, the `PATH` environment variable
/// will be searched in an OS-defined way.
///
/// # Platform-specific behavior
///
/// Note on Windows: For executable files with the .exe extension,
/// it can be omitted when specifying the program for this Command.
/// However, if the file has a different extension,
/// a filename including the extension needs to be provided,
/// otherwise the file won't be found.
/// The details below describe the current behavior, but these details
/// may change in future versions of Rust.
///
/// On Unix, the `PATH` searched comes from the child's environment:
///
/// - If the environment is unmodified, the child inherits the parent's
/// `PATH` and that is what is searched.
/// - If `PATH` is explicitly set via [`env`], that new value is searched.
/// - If [`env_clear`] or [`env_remove`] removes `PATH` without a
/// replacement, `execvp` falls back to an OS-defined default (typically
/// `/bin:/usr/bin`), **not** the parent's `PATH`. This may fail to find
/// programs that rely on the parent's `PATH`.
///
/// To avoid surprises, use an absolute path or explicitly set `PATH` on
/// the `Command` when modifying the child's environment.
///
/// On Windows, Rust resolves the executable path before spawning, rather
/// than passing the name to `CreateProcessW` for resolution. When
/// `program` is not an absolute path, the following locations are searched
/// in order:
///
/// 1. The child's `PATH`, if explicitly set via [`env`].
/// 2. The directory of the current executable.
/// 3. The system directory (`GetSystemDirectoryW`).
/// 4. The Windows directory (`GetWindowsDirectoryW`).
/// 5. The parent process's `PATH`.
///
/// Note: when `PATH` is cleared via [`env_clear`] or [`env_remove`] on
/// Windows, step 1 is skipped but the parent process's `PATH` is still
/// searched at step 5, unlike on Unix.
///
/// For executable files, the `.exe` extension may be omitted. Files with
/// other extensions must include the extension, otherwise they will not be
/// found. Note that this behavior has some known limitations
/// (see issue #37519).
///
/// [`env`]: Self::env
/// [`env_remove`]: Self::env_remove
/// [`env_clear`]: Self::env_clear
///
/// # Examples
///