Skip to content

Commit 44f98ba

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. Discussed in: - #149070 - rust-lang/libs-team#194
1 parent 3d461af commit 44f98ba

File tree

7 files changed

+96
-7
lines changed

7 files changed

+96
-7
lines changed

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;
@@ -1157,7 +1157,8 @@ impl Command {
11571157
/// [`Command::env_remove`] can be retrieved with this method.
11581158
///
11591159
/// Note that this output does not include environment variables inherited from the parent
1160-
/// process.
1160+
/// process. To see the full list of environment variables, including those inherited from the
1161+
/// parent process, use [`Command::get_resolved_envs`].
11611162
///
11621163
/// Each element is a tuple key/value pair `(&OsStr, Option<&OsStr>)`. A [`None`] value
11631164
/// indicates its key was explicitly removed via [`Command::env_remove`]. The associated key for
@@ -1186,6 +1187,42 @@ impl Command {
11861187
CommandEnvs { iter: self.inner.get_envs() }
11871188
}
11881189

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

101+
pub fn get_resolved_envs(&self) -> ResolvedEnvs {
102+
ResolvedEnvs::new(self.env.capture())
103+
}
104+
101105
pub fn get_current_dir(&self) -> Option<&Path> {
102106
self.cwd.as_ref().map(Path::new)
103107
}

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};
@@ -83,6 +83,10 @@ impl Command {
8383
self.env.iter()
8484
}
8585

86+
pub fn get_resolved_envs(&self) -> ResolvedEnvs {
87+
ResolvedEnvs::new(self.env.capture())
88+
}
89+
8690
pub fn get_current_dir(&self) -> Option<&Path> {
8791
None
8892
}

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::{self, AnonPipe};
18-
use crate::sys::process::env::{CommandEnv, CommandEnvs};
18+
use crate::sys::process::env::{CommandEnv, CommandEnvs, ResolvedEnvs};
1919
use crate::sys_common::{FromInner, IntoInner};
2020
use crate::{fmt, io};
2121

@@ -263,6 +263,10 @@ impl Command {
263263
self.env.iter()
264264
}
265265

266+
pub fn get_resolved_envs(&self) -> ResolvedEnvs {
267+
ResolvedEnvs::new(self.env.capture())
268+
}
269+
266270
pub fn get_current_dir(&self) -> Option<&Path> {
267271
self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes())))
268272
}

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;
@@ -86,6 +86,10 @@ impl Command {
8686
self.env.iter()
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
self.cwd.as_ref().map(|cs| Path::new(cs))
9195
}

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};
@@ -250,6 +250,10 @@ impl Command {
250250
self.env.iter()
251251
}
252252

253+
pub fn get_resolved_envs(&self) -> ResolvedEnvs {
254+
ResolvedEnvs::new(self.env.capture())
255+
}
256+
253257
pub fn get_current_dir(&self) -> Option<&Path> {
254258
self.cwd.as_ref().map(Path::new)
255259
}

0 commit comments

Comments
 (0)