diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index 545dd687d..83927b160 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -28,6 +28,7 @@ A mere - implies -i. If no COMMAND, print the resulting environment. struct Options<'a> { ignore_env: bool, null: bool, + running_directory: Option<&'a str>, files: Vec<&'a str>, unsets: Vec<&'a str>, sets: Vec<(&'a str, &'a str)>, @@ -128,6 +129,13 @@ fn create_app() -> App<'static, 'static> { .short("i") .long("ignore-environment") .help("start with an empty environment")) + .arg(Arg::with_name("chdir") + .short("c") + .long("chdir") + .takes_value(true) + .number_of_values(1) + .value_name("DIR") + .help("change working directory to DIR")) .arg(Arg::with_name("null") .short("0") .long("null") @@ -158,6 +166,7 @@ fn run_env(args: impl uucore::Args) -> Result<(), i32> { let ignore_env = matches.is_present("ignore-environment"); let null = matches.is_present("null"); + let running_directory = matches.value_of("chdir"); let files = matches .values_of("file") .map(Iterator::collect) @@ -170,12 +179,24 @@ fn run_env(args: impl uucore::Args) -> Result<(), i32> { let mut opts = Options { ignore_env, null, + running_directory, files, unsets, sets: vec![], program: vec![], }; + // change directory + if let Some(d) = opts.running_directory { + match env::set_current_dir(d) { + Ok(()) => d, + Err(error) => { + eprintln!("env: cannot change directory to \"{}\": {}", d, error); + return Err(125); + } + }; + } + // we handle the name, value pairs and the program to be executed by treating them as external // subcommands in clap if let (external, Some(matches)) = matches.subcommand() { diff --git a/tests/by-util/test_env.rs b/tests/by-util/test_env.rs index fde35e726..f747f041e 100644 --- a/tests/by-util/test_env.rs +++ b/tests/by-util/test_env.rs @@ -1,4 +1,11 @@ +#[cfg(not(windows))] +use std::fs; + +use std::path::Path; +extern crate tempfile; +use self::tempfile::tempdir; use crate::common::util::*; +use std::env; #[test] fn test_env_help() { @@ -150,3 +157,64 @@ fn test_fail_null_with_program() { let out = new_ucmd!().arg("--null").arg("cd").fails().stderr; assert!(out.contains("cannot specify --null (-0) with command")); } + +#[cfg(not(windows))] +#[test] +fn test_change_directory() { + let scene = TestScenario::new(util_name!()); + let temporary_directory = tempdir().unwrap(); + let temporary_path = fs::canonicalize(temporary_directory.path()).unwrap(); + assert_ne!(env::current_dir().unwrap(), temporary_path); + + // command to print out current working directory + let pwd = "pwd"; + + let out = scene + .ucmd() + .arg("--chdir") + .arg(&temporary_path) + .arg(pwd) + .run() + .stdout; + assert_eq!(out.trim(), temporary_path.as_os_str()) +} + +// no way to consistently get "current working directory", `cd` doesn't work @ CI +// instead, we test that the unique temporary directory appears somewhere in the printed variables +#[cfg(windows)] +#[test] +fn test_change_directory() { + let scene = TestScenario::new(util_name!()); + let temporary_directory = tempdir().unwrap(); + let temporary_path = temporary_directory.path(); + + assert_ne!(env::current_dir().unwrap(), temporary_path); + + let out = scene + .ucmd() + .arg("--chdir") + .arg(&temporary_path) + .run() + .stdout; + assert_eq!( + out.lines() + .any(|line| line.ends_with(temporary_path.file_name().unwrap().to_str().unwrap())), + false + ); +} + +#[test] +fn test_fail_change_directory() { + let scene = TestScenario::new(util_name!()); + let some_non_existing_path = "some_nonexistent_path"; + assert_eq!(Path::new(some_non_existing_path).is_dir(), false); + + let out = scene + .ucmd() + .arg("--chdir") + .arg(some_non_existing_path) + .arg("pwd") + .fails() + .stderr; + assert!(out.contains("env: cannot change directory to ")); +}