1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-27 19:17:43 +00:00

Merge pull request #1316 from rivy/fix.env

fix ~ env: support windows commands (BAT/CMD, builtins)
This commit is contained in:
Alex Lyon 2019-04-29 14:04:53 -07:00 committed by GitHub
commit d448fd81c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 111 additions and 4 deletions

1
src/env/Cargo.toml vendored
View file

@ -11,6 +11,7 @@ path = "env.rs"
[dependencies]
libc = "0.2.42"
uucore = { path="../uucore" }
rust-ini = "0.13.0"
[[bin]]
name = "env"

60
src/env/env.rs vendored
View file

@ -13,8 +13,11 @@
#[macro_use]
extern crate uucore;
extern crate ini;
use ini::Ini;
use std::env;
use std::io::{stdout, Write};
use std::io::{stdin, stdout, Write};
use std::process::Command;
static NAME: &str = "env";
@ -27,6 +30,7 @@ static LONG_HELP: &str = "
struct Options {
ignore_env: bool,
null: bool,
files: Vec<String>,
unsets: Vec<String>,
sets: Vec<(String, String)>,
program: Vec<String>,
@ -40,6 +44,17 @@ fn print_env(null: bool) {
}
}
#[cfg(not(windows))]
fn build_command(mut args: Vec<String>) -> (String, Vec<String>) {
(args.remove(0), args)
}
#[cfg(windows)]
fn build_command(mut args: Vec<String>) -> (String, Vec<String>) {
args.insert(0, "/d/c".to_string());
(env::var("ComSpec").unwrap_or("cmd".to_string()), args)
}
pub fn uumain(args: Vec<String>) -> i32 {
let mut core_opts = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP);
core_opts
@ -49,12 +64,14 @@ pub fn uumain(args: Vec<String>) -> i32 {
"null",
"end each output line with a 0 byte rather than newline (only valid when printing the environment)",
)
.optopt("f", "file", "read and sets variables from the file (prior to sets/unsets)", "FILE")
.optopt("u", "unset", "remove variable from the environment", "NAME");
let mut opts = Box::new(Options {
ignore_env: false,
null: false,
unsets: vec![],
files: vec![],
sets: vec![],
program: vec![],
});
@ -99,6 +116,14 @@ pub fn uumain(args: Vec<String>) -> i32 {
"--ignore-environment" => opts.ignore_env = true,
"--null" => opts.null = true,
"--file" => {
let var = iter.next();
match var {
None => println!("{}: this option requires an argument: {}", NAME, opt),
Some(s) => opts.files.push(s.to_owned()),
}
}
"--unset" => {
let var = iter.next();
@ -130,6 +155,14 @@ pub fn uumain(args: Vec<String>) -> i32 {
match c {
'i' => opts.ignore_env = true,
'0' => opts.null = true,
'f' => {
let var = iter.next();
match var {
None => println!("{}: this option requires an argument: {}", NAME, opt),
Some(s) => opts.files.push(s.to_owned()),
}
}
'u' => {
let var = iter.next();
@ -189,6 +222,28 @@ pub fn uumain(args: Vec<String>) -> i32 {
}
}
for file in &opts.files {
let conf = if file == "-" {
let stdin = stdin();
let mut stdin_locked = stdin.lock();
Ini::read_from(&mut stdin_locked)
} else {
Ini::load_from_file(file)
};
let conf = match conf {
Ok(config) => config,
Err(error) => {
eprintln!("env: error: \"{}\": {}", file, error);
return 1;
}
};
for (_, prop) in &conf {
for (key, value) in prop {
env::set_var(key, value);
}
}
}
for name in &opts.unsets {
env::remove_var(name);
}
@ -198,8 +253,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
}
if !opts.program.is_empty() {
let prog = opts.program[0].clone();
let args = &opts.program[1..];
let (prog, args) = build_command(opts.program);
match Command::new(prog).args(args).status() {
Ok(exit) => {
return if exit.success() {

View file

@ -447,6 +447,7 @@ impl TestScenario {
/// 2. it tracks arguments provided so that in test cases which may provide variations of an arg in loops
/// the test failure can display the exact call which preceded an assertion failure.
/// 3. it provides convenience construction arguments to set the Command working directory and/or clear its environment.
#[derive(Debug)]
pub struct UCommand {
pub raw: Command,
comm_string: String,

4
tests/fixtures/env/vars.conf.txt vendored Normal file
View file

@ -0,0 +1,4 @@
# comment
FOO=bar
BAR="bamf this"

View file

@ -1,6 +1,5 @@
use common::util::*;
#[test]
fn test_env_help() {
assert!(new_ucmd!().arg("--help").succeeds().no_stderr().stdout.contains("Options:"));
@ -11,6 +10,54 @@ fn test_env_version() {
assert!(new_ucmd!().arg("--version").succeeds().no_stderr().stdout.contains(util_name!()));
}
#[test]
fn test_echo() {
// assert!(new_ucmd!().arg("printf").arg("FOO-bar").succeeds().no_stderr().stdout.contains("FOO-bar"));
let mut cmd = new_ucmd!();
cmd.arg("echo").arg("FOO-bar");
println!("cmd={:?}", cmd);
let result = cmd.run();
println!("success={:?}", result.success);
println!("stdout={:?}", result.stdout);
println!("stderr={:?}", result.stderr);
assert!(result.success);
let out = result.stdout.trim_right();
assert_eq!(out, "FOO-bar");
}
#[test]
fn test_file_option() {
let out = new_ucmd!()
.arg("-f").arg("vars.conf.txt")
.run().stdout;
assert_eq!(out.lines().filter(|&line| line == "FOO=bar" || line == "BAR=bamf this").count(), 2);
}
#[test]
fn test_combined_file_set() {
let out = new_ucmd!()
.arg("-f").arg("vars.conf.txt")
.arg("FOO=bar.alt")
.run().stdout;
assert_eq!(out.lines().filter(|&line| line == "FOO=bar.alt").count(), 1);
}
#[test]
fn test_combined_file_set_unset() {
let out = new_ucmd!()
.arg("-u").arg("BAR")
.arg("-f").arg("vars.conf.txt")
.arg("FOO=bar.alt")
.run().stdout;
assert_eq!(out.lines().filter(|&line| line == "FOO=bar.alt" || line.starts_with("BAR=")).count(), 1);
}
#[test]
fn test_single_name_value_pair() {
let out = new_ucmd!()