Skip to content

Commit ebf31e4

Browse files
Rollup merge of rust-lang#149362 - schneems:schneems/get_resolved_envs, r=Mark-Simulacrum
Add Command::get_resolved_envs This addition allows an end-user to inspect the environment variables that are visible to the process when it boots. Discussed in: - Tracking issue: rust-lang#149070 (partially closes) - ACP: rust-lang/libs-team#194
2 parents add2cb6 + 5ed5b26 commit ebf31e4

8 files changed

Lines changed: 99 additions & 6 deletions

File tree

library/std/src/process.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1185,7 +1185,8 @@ impl Command {
11851185
/// [`Command::env_remove`] can be retrieved with this method.
11861186
///
11871187
/// Note that this output does not include environment variables inherited from the parent
1188-
/// process.
1188+
/// process. To see the full list of environment variables, including those inherited from the
1189+
/// parent process, use [`Command::get_resolved_envs`].
11891190
///
11901191
/// Each element is a tuple key/value pair `(&OsStr, Option<&OsStr>)`. A [`None`] value
11911192
/// indicates its key was explicitly removed via [`Command::env_remove`]. The associated key for
@@ -1214,6 +1215,42 @@ impl Command {
12141215
CommandEnvs { iter: self.inner.get_envs() }
12151216
}
12161217

1218+
/// Returns an iterator of the environment variables that will be set when the process is spawned.
1219+
///
1220+
/// This returns the environment as it would be if the command were executed at the time of calling
1221+
/// this method. The returned environment includes:
1222+
/// - All inherited environment variables from the parent process (unless [`Command::env_clear`] was called)
1223+
/// - All environment variables explicitly set via [`Command::env`] or [`Command::envs`]
1224+
/// - Excluding any environment variables removed via [`Command::env_remove`]
1225+
///
1226+
/// Note that the returned environment is a snapshot at the time this method is called and will not
1227+
/// reflect any subsequent changes to the `Command` or the parent process's environment. Additionally,
1228+
/// it will not reflect changes made in a `pre_exec` hook (on Unix platforms).
1229+
///
1230+
/// Each element is a tuple `(OsString, OsString)` representing an environment variable key and value.
1231+
///
1232+
/// # Examples
1233+
///
1234+
/// ```
1235+
/// #![feature(command_resolved_envs)]
1236+
/// use std::process::Command;
1237+
/// use std::ffi::{OsString, OsStr};
1238+
/// use std::env;
1239+
/// use std::collections::HashMap;
1240+
///
1241+
/// let mut cmd = Command::new("ls");
1242+
/// cmd.env("TZ", "UTC");
1243+
/// unsafe { env::set_var("EDITOR", "vim"); }
1244+
///
1245+
/// let resolved: HashMap<OsString, OsString> = cmd.get_resolved_envs().collect();
1246+
/// assert_eq!(resolved.get(OsStr::new("TZ")), Some(&OsString::from("UTC")));
1247+
/// assert_eq!(resolved.get(OsStr::new("EDITOR")), Some(&OsString::from("vim")));
1248+
/// ```
1249+
#[unstable(feature = "command_resolved_envs", issue = "149070")]
1250+
pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
1251+
self.inner.get_resolved_envs()
1252+
}
1253+
12171254
/// Returns the working directory for the child process.
12181255
///
12191256
/// This returns [`None`] if the working directory will not be changed.
@@ -1367,6 +1404,9 @@ impl<'a> fmt::Debug for CommandEnvs<'a> {
13671404
}
13681405
}
13691406

1407+
#[unstable(feature = "command_resolved_envs", issue = "149070")]
1408+
pub use imp::CommandResolvedEnvs;
1409+
13701410
/// The output of a finished process.
13711411
///
13721412
/// This is returned in a Result by either the [`output`] method of a

library/std/src/sys/process/env.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,34 @@ impl<'a> ExactSizeIterator for CommandEnvs<'a> {
113113
self.iter.is_empty()
114114
}
115115
}
116+
117+
/// An iterator over the fully resolved environment variables.
118+
///
119+
/// This struct is created by
120+
/// [`Command::get_resolved_envs`][crate::process::Command::get_resolved_envs]. See its
121+
/// documentation for more.
122+
#[derive(Debug)]
123+
#[must_use = "iterators are lazy and do nothing unless consumed"]
124+
#[unstable(feature = "command_resolved_envs", issue = "149070")]
125+
pub struct CommandResolvedEnvs {
126+
inner: crate::collections::btree_map::IntoIter<EnvKey, OsString>,
127+
}
128+
129+
impl CommandResolvedEnvs {
130+
pub(crate) fn new(map: BTreeMap<EnvKey, OsString>) -> Self {
131+
Self { inner: map.into_iter() }
132+
}
133+
}
134+
135+
#[unstable(feature = "command_resolved_envs", issue = "149070")]
136+
impl Iterator for CommandResolvedEnvs {
137+
type Item = (OsString, OsString);
138+
139+
fn next(&mut self) -> Option<Self::Item> {
140+
self.inner.next().map(|(key, value)| (key.into(), value))
141+
}
142+
143+
fn size_hint(&self) -> (usize, Option<usize>) {
144+
self.inner.size_hint()
145+
}
146+
}

library/std/src/sys/process/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ cfg_select! {
2727
mod env;
2828

2929
pub use env::CommandEnvs;
30+
#[unstable(feature = "command_resolved_envs", issue = "149070")]
31+
pub use env::CommandResolvedEnvs;
3032
#[cfg(target_family = "unix")]
3133
pub use imp::getppid;
3234
pub use imp::{

library/std/src/sys/process/motor.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::CommandEnvs;
2-
use super::env::CommandEnv;
2+
use super::env::{CommandEnv, CommandResolvedEnvs};
33
use crate::ffi::OsStr;
44
pub use crate::ffi::OsString as EnvKey;
55
use crate::num::NonZeroI32;
@@ -100,6 +100,10 @@ impl Command {
100100
self.env.does_clear()
101101
}
102102

103+
pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
104+
CommandResolvedEnvs::new(self.env.capture())
105+
}
106+
103107
pub fn get_current_dir(&self) -> Option<&Path> {
104108
self.cwd.as_ref().map(Path::new)
105109
}

library/std/src/sys/process/uefi.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use r_efi::protocols::{simple_text_input, simple_text_output};
22

3-
use super::env::{CommandEnv, CommandEnvs};
3+
use super::env::{CommandEnv, CommandEnvs, CommandResolvedEnvs};
44
use crate::collections::BTreeMap;
55
pub use crate::ffi::OsString as EnvKey;
66
use crate::ffi::{OsStr, OsString};
@@ -86,6 +86,10 @@ impl Command {
8686
self.env.does_clear()
8787
}
8888

89+
pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
90+
CommandResolvedEnvs::new(self.env.capture())
91+
}
92+
8993
pub fn get_current_dir(&self) -> Option<&Path> {
9094
None
9195
}

library/std/src/sys/process/unix/common.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::sys::fs::File;
1515
#[cfg(not(target_os = "fuchsia"))]
1616
use crate::sys::fs::OpenOptions;
1717
use crate::sys::pipe::pipe;
18-
use crate::sys::process::env::{CommandEnv, CommandEnvs};
18+
use crate::sys::process::env::{CommandEnv, CommandEnvs, CommandResolvedEnvs};
1919
use crate::sys::{FromInner, IntoInner, cvt_r};
2020
use crate::{fmt, io, mem};
2121

@@ -267,6 +267,10 @@ impl Command {
267267
self.env.does_clear()
268268
}
269269

270+
pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
271+
CommandResolvedEnvs::new(self.env.capture())
272+
}
273+
270274
pub fn get_current_dir(&self) -> Option<&Path> {
271275
self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes())))
272276
}

library/std/src/sys/process/unsupported.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::env::{CommandEnv, CommandEnvs};
1+
use super::env::{CommandEnv, CommandEnvs, CommandResolvedEnvs};
22
pub use crate::ffi::OsString as EnvKey;
33
use crate::ffi::{OsStr, OsString};
44
use crate::num::NonZero;
@@ -89,6 +89,10 @@ impl Command {
8989
self.env.does_clear()
9090
}
9191

92+
pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
93+
CommandResolvedEnvs::new(self.env.capture())
94+
}
95+
9296
pub fn get_current_dir(&self) -> Option<&Path> {
9397
self.cwd.as_ref().map(|cs| Path::new(cs))
9498
}

library/std/src/sys/process/windows.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ mod tests;
55

66
use core::ffi::c_void;
77

8-
use super::env::{CommandEnv, CommandEnvs};
8+
use super::env::{CommandEnv, CommandEnvs, CommandResolvedEnvs};
99
use crate::collections::BTreeMap;
1010
use crate::env::consts::{EXE_EXTENSION, EXE_SUFFIX};
1111
use crate::ffi::{OsStr, OsString};
@@ -256,6 +256,10 @@ impl Command {
256256
self.env.does_clear()
257257
}
258258

259+
pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
260+
CommandResolvedEnvs::new(self.env.capture())
261+
}
262+
259263
pub fn get_current_dir(&self) -> Option<&Path> {
260264
self.cwd.as_ref().map(Path::new)
261265
}

0 commit comments

Comments
 (0)