Change:
currently only outputs the program and it's arguments. It should also output the environment variables and the current path, as set by , and . This would help with figuring out what's going on :-)
We also currently have no way to extract most of this information without utilizing Debug, so out of tree implementations can't be implemented.
Would/should this be the specific additions to the environment set by , or the actual environment? The most straightforward implementation of this (debug-printing the individual fields of the internal struct) seems to do the latter. That is, this patch (in )— results in program behavior like this (scroll right to see the very bulky output; should be verbose, but how verbose is too verbose??):
I'd be fine with the verbose output. The debug output is only meant for debug purposes and generating the full environment saves us from oh-that's-why-it's-not-working-this-stupid-thing moments
Seems reasonable. I would be ready to consider a PR implementing this.
Triage: no changes here. I've actually wanted this too, but don't have the bandwidth to work on it, personally.

Discussion:
/// Format the program and arguments of a Command for display. Any /// non-utf8 data is lossily converted using the utf8 replacement /// character. <ins> /// /// The default format approximates a shell invocation of the program along with its /// arguments. It does not include most of the other command properties. The output is not guaranteed to work /// (e.g. due to lack of shell-escaping or differences in path resolution) /// On some platforms you can use [the alternate syntax] to show more fields. /// /// Note that the debug implementation is platform-specific. /// /// [the alternate syntax]: fmt#sign0 </ins> fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) }

assert!(p.is_ok()); } <ins> #[test] #[cfg(not(windows))] #[cfg_attr(any(target_os = "emscripten", target_env = "sgx"), ignore)] fn main() { const PIDFD: &'static str = if cfg!(target_os = "linux") { "    create_pidfd: false,n" } else { "" }; let mut command = Command::new("some-boring-name"); assert_eq!(format!("{command:?}"), format!(r#""some-boring-name""#)); assert_eq!( format!("{command:#?}"), format!( r#"Command {{ program: "some-boring-name", args: [ "some-boring-name", ], {PIDFD}}}"# ) ); command.args(&["1", "2", "3"]); assert_eq!(format!("{command:?}"), format!(r#""some-boring-name" "1" "2" "3""#)); assert_eq!( format!("{command:#?}"), format!( r#"Command {{ program: "some-boring-name", args: [ "some-boring-name", "1", "2", "3", ], {PIDFD}}}"# ) ); crate::os::unix::process::CommandExt::arg0(&mut command, "exciting-name"); assert_eq!( format!("{command:?}"), format!(r#"["some-boring-name"] "exciting-name" "1" "2" "3""#) ); assert_eq!( format!("{command:#?}"), format!( r#"Command {{ program: "some-boring-name", args: [ "exciting-name", "1", "2", "3", ], {PIDFD}}}"# ) ); let mut command_with_env_and_cwd = Command::new("boring-name"); command_with_env_and_cwd.current_dir("/some/path").env("FOO", "bar"); assert_eq!( format!("{command_with_env_and_cwd:?}"), r#"cd "/some/path" && FOO="bar" "boring-name""# ); assert_eq!( format!("{command_with_env_and_cwd:#?}"), format!( r#"Command {{ program: "boring-name", args: [ "boring-name", ], env: CommandEnv {{ clear: false, vars: {{ "FOO": Some( "bar", ), }}, }}, cwd: Some( "/some/path", ), {PIDFD}}}"# ) ); } </ins> // See issue #91991 #[test] #[cfg(windows)]

Null, } <ins> #[derive(Debug)] </ins> pub enum Stdio { Inherit, Null,

} impl fmt::Debug for Command { <ins> // show all attributes but `self.closures` which does not implement `Debug` // and `self.argv` which is not useful for debugging </ins> fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { <del> if self.program != self.args[0] { write!(f, "[{:?}] ", self.program)?; } write!(f, "{:?}", self.args[0])?; </del> <ins> if f.alternate() { let mut debug_command = f.debug_struct("Command"); debug_command.field("program", &self.program).field("args", &self.args); if !self.env.is_unchanged() { debug_command.field("env", &self.env); } if self.cwd.is_some() { debug_command.field("cwd", &self.cwd); } if self.uid.is_some() { debug_command.field("uid", &self.uid); } if self.gid.is_some() { debug_command.field("gid", &self.gid); } if self.groups.is_some() { debug_command.field("groups", &self.groups); } if self.stdin.is_some() { debug_command.field("stdin", &self.stdin); } if self.stdout.is_some() { debug_command.field("stdout", &self.stdout); } if self.stderr.is_some() { debug_command.field("stderr", &self.stderr); } if self.pgroup.is_some() { debug_command.field("pgroup", &self.pgroup); } #[cfg(target_os = "linux")] { debug_command.field("create_pidfd", &self.create_pidfd); } </ins> <del> for arg in &self.args[1..] { write!(f, " {:?}", arg)?; </del> <ins> debug_command.finish() } else { if let Some(ref cwd) = self.cwd { write!(f, "cd {cwd:?} && ")?; } for (key, value_opt) in self.get_envs() { if let Some(value) = value_opt { write!(f, "{}={value:?} ", key.to_string_lossy())?; } } if self.program != self.args[0] { write!(f, "[{:?}] ", self.program)?; } write!(f, "{:?}", self.args[0])?; for arg in &self.args[1..] { write!(f, " {:?}", arg)?; } Ok(()) </ins> } <del> Ok(()) </del> } }

use crate::collections::BTreeMap; use crate::env; use crate::ffi::{OsStr, OsString}; <ins> use crate::fmt; </ins> use crate::io; use crate::sys::pipe::read2; use crate::sys::process::{EnvKey, ExitStatus, Process, StdioPipes}; // Stores a set of changes to an environment <del> #[derive(Clone, Debug)] </del> <ins> #[derive(Clone)] </ins> pub struct CommandEnv { clear: bool, saw_path: bool,

} } <ins> impl fmt::Debug for CommandEnv { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut debug_command_env = f.debug_struct("CommandEnv"); debug_command_env.field("clear", &self.clear).field("vars", &self.vars); debug_command_env.finish() } } </ins> impl CommandEnv { // Capture the current environment with these changes applied pub fn capture(&self) -> BTreeMap<EnvKey, OsString> {

<del> // run-pass // ignore-windows - this is a unix-specific test // ignore-emscripten no processes // ignore-sgx no processes use std::os::unix::process::CommandExt; use std::process::Command; fn main() { let mut command = Command::new("some-boring-name"); assert_eq!(format!("{:?}", command), r#""some-boring-name""#); command.args(&["1", "2", "3"]); assert_eq!(format!("{:?}", command), r#""some-boring-name" "1" "2" "3""#); command.arg0("exciting-name"); assert_eq!(format!("{:?}", command), r#"["some-boring-name"] "exciting-name" "1" "2" "3""#); } </del>