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:
commit
d448fd81c5
5 changed files with 111 additions and 4 deletions
1
src/env/Cargo.toml
vendored
1
src/env/Cargo.toml
vendored
|
@ -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
60
src/env/env.rs
vendored
|
@ -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() {
|
||||
|
|
|
@ -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
4
tests/fixtures/env/vars.conf.txt
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
# comment
|
||||
FOO=bar
|
||||
|
||||
BAR="bamf this"
|
|
@ -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!()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue