Skip to content

Commit f47b0bb

Browse files
committed
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.
1 parent 86a49fd commit f47b0bb

7 files changed

Lines changed: 96 additions & 7 deletions

File tree

library/std/src/process.rs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@
161161
mod tests;
162162

163163
use crate::convert::Infallible;
164-
use crate::ffi::OsStr;
164+
use crate::ffi::{OsStr, OsString};
165165
use crate::io::prelude::*;
166166
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
167167
use crate::num::NonZero;
@@ -1155,7 +1155,8 @@ impl Command {
11551155
/// [`Command::env_remove`] can be retrieved with this method.
11561156
///
11571157
/// Note that this output does not include environment variables inherited from the parent
1158-
/// process.
1158+
/// process. To see the full list of environment variables, including those inherited from the
1159+
/// parent process, use [`Command::get_resolved_envs`].
11591160
///
11601161
/// Each element is a tuple key/value pair `(&OsStr, Option<&OsStr>)`. A [`None`] value
11611162
/// indicates its key was explicitly removed via [`Command::env_remove`]. The associated key for
@@ -1184,6 +1185,42 @@ impl Command {
11841185
CommandEnvs { iter: self.inner.get_envs() }
11851186
}
11861187

1188+
/// Returns an iterator of the environment variables that will be set when the process is spawned.
1189+
///
1190+
/// This returns the environment as it would be if the command were executed at the time of calling
1191+
/// this method. The returned environment includes:
1192+
/// - All inherited environment variables from the parent process (unless [`Command::env_clear`] was called)
1193+
/// - All environment variables explicitly set via [`Command::env`] or [`Command::envs`]
1194+
/// - Excluding any environment variables removed via [`Command::env_remove`]
1195+
///
1196+
/// Note that the returned environment is a snapshot at the time this method is called and will not
1197+
/// reflect any subsequent changes to the `Command` or the parent process's environment. Additionally,
1198+
/// it will not reflect changes made in a `pre_exec` hook (on Unix platforms).
1199+
///
1200+
/// Each element is a tuple `(OsString, OsString)` representing an environment variable key and value.
1201+
///
1202+
/// # Examples
1203+
///
1204+
/// ```
1205+
/// #![feature(command_get_resolved_envs)]
1206+
/// use std::process::Command;
1207+
/// use std::ffi::{OsString, OsStr};
1208+
/// use std::env;
1209+
/// use std::collections::HashMap;
1210+
///
1211+
/// let mut cmd = Command::new("ls");
1212+
/// cmd.env("TZ", "UTC");
1213+
/// unsafe { env::set_var("EDITOR", "vim"); }
1214+
///
1215+
/// let resolved: HashMap<OsString, OsString> = cmd.get_resolved_envs().collect();
1216+
/// assert_eq!(resolved.get(OsStr::new("TZ")), Some(&OsString::from("UTC")));
1217+
/// assert_eq!(resolved.get(OsStr::new("EDITOR")), Some(&OsString::from("vim")));
1218+
/// ```
1219+
#[unstable(feature = "command_get_resolved_envs", issue = "149070")]
1220+
pub fn get_resolved_envs(&self) -> impl Iterator<Item = (OsString, OsString)> {
1221+
self.inner.get_resolved_envs()
1222+
}
1223+
11871224
/// Returns the working directory for the child process.
11881225
///
11891226
/// This returns [`None`] if the working directory will not be changed.

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,35 @@ 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 returned by `Command::get_resolved_envs`.
120+
#[derive(Debug)]
121+
pub struct ResolvedEnvs {
122+
inner: crate::collections::btree_map::IntoIter<EnvKey, OsString>,
123+
}
124+
125+
impl ResolvedEnvs {
126+
pub(crate) fn new(map: BTreeMap<EnvKey, OsString>) -> Self {
127+
Self { inner: map.into_iter() }
128+
}
129+
}
130+
131+
impl Iterator for ResolvedEnvs {
132+
type Item = (OsString, OsString);
133+
134+
fn next(&mut self) -> Option<Self::Item> {
135+
self.inner.next().map(|(key, value)| (key.into(), value))
136+
}
137+
138+
fn size_hint(&self) -> (usize, Option<usize>) {
139+
self.inner.size_hint()
140+
}
141+
}
142+
143+
impl ExactSizeIterator for ResolvedEnvs {
144+
fn len(&self) -> usize {
145+
self.inner.len()
146+
}
147+
}

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, ResolvedEnvs};
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) -> ResolvedEnvs {
104+
ResolvedEnvs::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, ResolvedEnvs};
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) -> ResolvedEnvs {
90+
ResolvedEnvs::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, ResolvedEnvs};
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) -> ResolvedEnvs {
271+
ResolvedEnvs::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, ResolvedEnvs};
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) -> ResolvedEnvs {
93+
ResolvedEnvs::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, ResolvedEnvs};
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) -> ResolvedEnvs {
260+
ResolvedEnvs::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)