Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion library/std/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1155,7 +1155,8 @@ impl Command {
/// [`Command::env_remove`] can be retrieved with this method.
///
/// Note that this output does not include environment variables inherited from the parent
/// process.
/// process. To see the full list of environment variables, including those inherited from the
/// parent process, use [`Command::get_resolved_envs`].
///
/// Each element is a tuple key/value pair `(&OsStr, Option<&OsStr>)`. A [`None`] value
/// indicates its key was explicitly removed via [`Command::env_remove`]. The associated key for
Expand Down Expand Up @@ -1184,6 +1185,42 @@ impl Command {
CommandEnvs { iter: self.inner.get_envs() }
}

/// Returns an iterator of the environment variables that will be set when the process is spawned.
///
/// This returns the environment as it would be if the command were executed at the time of calling
/// this method. The returned environment includes:
/// - All inherited environment variables from the parent process (unless [`Command::env_clear`] was called)
/// - All environment variables explicitly set via [`Command::env`] or [`Command::envs`]
/// - Excluding any environment variables removed via [`Command::env_remove`]
///
/// Note that the returned environment is a snapshot at the time this method is called and will not
/// reflect any subsequent changes to the `Command` or the parent process's environment. Additionally,
/// it will not reflect changes made in a `pre_exec` hook (on Unix platforms).
///
/// Each element is a tuple `(OsString, OsString)` representing an environment variable key and value.
///
/// # Examples
///
/// ```
/// #![feature(command_resolved_envs)]
/// use std::process::Command;
/// use std::ffi::{OsString, OsStr};
/// use std::env;
/// use std::collections::HashMap;
///
/// let mut cmd = Command::new("ls");
/// cmd.env("TZ", "UTC");
/// unsafe { env::set_var("EDITOR", "vim"); }
///
/// let resolved: HashMap<OsString, OsString> = cmd.get_resolved_envs().collect();
/// assert_eq!(resolved.get(OsStr::new("TZ")), Some(&OsString::from("UTC")));
/// assert_eq!(resolved.get(OsStr::new("EDITOR")), Some(&OsString::from("vim")));
/// ```
#[unstable(feature = "command_resolved_envs", issue = "149070")]
pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
self.inner.get_resolved_envs()
}

/// Returns the working directory for the child process.
///
/// This returns [`None`] if the working directory will not be changed.
Expand Down Expand Up @@ -1337,6 +1374,9 @@ impl<'a> fmt::Debug for CommandEnvs<'a> {
}
}

#[unstable(feature = "command_resolved_envs", issue = "149070")]
pub use imp::CommandResolvedEnvs;

/// The output of a finished process.
///
/// This is returned in a Result by either the [`output`] method of a
Expand Down
31 changes: 31 additions & 0 deletions library/std/src/sys/process/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,34 @@ impl<'a> ExactSizeIterator for CommandEnvs<'a> {
self.iter.is_empty()
}
}

/// An iterator over the fully resolved environment variables.
///
/// This struct is created by
/// [`Command::get_resolved_envs`][crate::process::Command::get_resolved_envs]. See its
/// documentation for more.
#[derive(Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[unstable(feature = "command_resolved_envs", issue = "149070")]
pub struct CommandResolvedEnvs {
inner: crate::collections::btree_map::IntoIter<EnvKey, OsString>,
}

impl CommandResolvedEnvs {
pub(crate) fn new(map: BTreeMap<EnvKey, OsString>) -> Self {
Self { inner: map.into_iter() }
}
}

#[unstable(feature = "command_resolved_envs", issue = "149070")]
impl Iterator for CommandResolvedEnvs {
type Item = (OsString, OsString);

fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(key, value)| (key.into(), value))
}

fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
2 changes: 2 additions & 0 deletions library/std/src/sys/process/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ cfg_select! {
mod env;

pub use env::CommandEnvs;
#[unstable(feature = "command_resolved_envs", issue = "149070")]
pub use env::CommandResolvedEnvs;
pub use imp::{
ChildPipe, Command, CommandArgs, EnvKey, ExitCode, ExitStatus, ExitStatusError, Process, Stdio,
read_output,
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/process/motor.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::CommandEnvs;
use super::env::CommandEnv;
use super::env::{CommandEnv, CommandResolvedEnvs};
use crate::ffi::OsStr;
pub use crate::ffi::OsString as EnvKey;
use crate::num::NonZeroI32;
Expand Down Expand Up @@ -100,6 +100,10 @@ impl Command {
self.env.does_clear()
}

pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
CommandResolvedEnvs::new(self.env.capture())
}

pub fn get_current_dir(&self) -> Option<&Path> {
self.cwd.as_ref().map(Path::new)
}
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/process/uefi.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use r_efi::protocols::{simple_text_input, simple_text_output};

use super::env::{CommandEnv, CommandEnvs};
use super::env::{CommandEnv, CommandEnvs, CommandResolvedEnvs};
use crate::collections::BTreeMap;
pub use crate::ffi::OsString as EnvKey;
use crate::ffi::{OsStr, OsString};
Expand Down Expand Up @@ -86,6 +86,10 @@ impl Command {
self.env.does_clear()
}

pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
CommandResolvedEnvs::new(self.env.capture())
}

pub fn get_current_dir(&self) -> Option<&Path> {
None
}
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/process/unix/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::sys::fs::File;
#[cfg(not(target_os = "fuchsia"))]
use crate::sys::fs::OpenOptions;
use crate::sys::pipe::pipe;
use crate::sys::process::env::{CommandEnv, CommandEnvs};
use crate::sys::process::env::{CommandEnv, CommandEnvs, CommandResolvedEnvs};
use crate::sys::{FromInner, IntoInner, cvt_r};
use crate::{fmt, io, mem};

Expand Down Expand Up @@ -267,6 +267,10 @@ impl Command {
self.env.does_clear()
}

pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
CommandResolvedEnvs::new(self.env.capture())
}

pub fn get_current_dir(&self) -> Option<&Path> {
self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes())))
}
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/process/unsupported.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::env::{CommandEnv, CommandEnvs};
use super::env::{CommandEnv, CommandEnvs, CommandResolvedEnvs};
pub use crate::ffi::OsString as EnvKey;
use crate::ffi::{OsStr, OsString};
use crate::num::NonZero;
Expand Down Expand Up @@ -89,6 +89,10 @@ impl Command {
self.env.does_clear()
}

pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
CommandResolvedEnvs::new(self.env.capture())
}

pub fn get_current_dir(&self) -> Option<&Path> {
self.cwd.as_ref().map(|cs| Path::new(cs))
}
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/process/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod tests;

use core::ffi::c_void;

use super::env::{CommandEnv, CommandEnvs};
use super::env::{CommandEnv, CommandEnvs, CommandResolvedEnvs};
use crate::collections::BTreeMap;
use crate::env::consts::{EXE_EXTENSION, EXE_SUFFIX};
use crate::ffi::{OsStr, OsString};
Expand Down Expand Up @@ -256,6 +256,10 @@ impl Command {
self.env.does_clear()
}

pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
CommandResolvedEnvs::new(self.env.capture())
}

pub fn get_current_dir(&self) -> Option<&Path> {
self.cwd.as_ref().map(Path::new)
}
Expand Down
Loading