mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27:44 +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]
|
[dependencies]
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { path="../uucore" }
|
uucore = { path="../uucore" }
|
||||||
|
rust-ini = "0.13.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "env"
|
name = "env"
|
||||||
|
|
60
src/env/env.rs
vendored
60
src/env/env.rs
vendored
|
@ -13,8 +13,11 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
|
extern crate ini;
|
||||||
|
|
||||||
|
use ini::Ini;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io::{stdout, Write};
|
use std::io::{stdin, stdout, Write};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
static NAME: &str = "env";
|
static NAME: &str = "env";
|
||||||
|
@ -27,6 +30,7 @@ static LONG_HELP: &str = "
|
||||||
struct Options {
|
struct Options {
|
||||||
ignore_env: bool,
|
ignore_env: bool,
|
||||||
null: bool,
|
null: bool,
|
||||||
|
files: Vec<String>,
|
||||||
unsets: Vec<String>,
|
unsets: Vec<String>,
|
||||||
sets: Vec<(String, String)>,
|
sets: Vec<(String, String)>,
|
||||||
program: Vec<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 {
|
pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
let mut core_opts = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP);
|
let mut core_opts = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP);
|
||||||
core_opts
|
core_opts
|
||||||
|
@ -49,12 +64,14 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
"null",
|
"null",
|
||||||
"end each output line with a 0 byte rather than newline (only valid when printing the environment)",
|
"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");
|
.optopt("u", "unset", "remove variable from the environment", "NAME");
|
||||||
|
|
||||||
let mut opts = Box::new(Options {
|
let mut opts = Box::new(Options {
|
||||||
ignore_env: false,
|
ignore_env: false,
|
||||||
null: false,
|
null: false,
|
||||||
unsets: vec![],
|
unsets: vec![],
|
||||||
|
files: vec![],
|
||||||
sets: vec![],
|
sets: vec![],
|
||||||
program: vec![],
|
program: vec![],
|
||||||
});
|
});
|
||||||
|
@ -99,6 +116,14 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
|
|
||||||
"--ignore-environment" => opts.ignore_env = true,
|
"--ignore-environment" => opts.ignore_env = true,
|
||||||
"--null" => opts.null = 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" => {
|
"--unset" => {
|
||||||
let var = iter.next();
|
let var = iter.next();
|
||||||
|
|
||||||
|
@ -130,6 +155,14 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
match c {
|
match c {
|
||||||
'i' => opts.ignore_env = true,
|
'i' => opts.ignore_env = true,
|
||||||
'0' => opts.null = 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' => {
|
'u' => {
|
||||||
let var = iter.next();
|
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 {
|
for name in &opts.unsets {
|
||||||
env::remove_var(name);
|
env::remove_var(name);
|
||||||
}
|
}
|
||||||
|
@ -198,8 +253,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !opts.program.is_empty() {
|
if !opts.program.is_empty() {
|
||||||
let prog = opts.program[0].clone();
|
let (prog, args) = build_command(opts.program);
|
||||||
let args = &opts.program[1..];
|
|
||||||
match Command::new(prog).args(args).status() {
|
match Command::new(prog).args(args).status() {
|
||||||
Ok(exit) => {
|
Ok(exit) => {
|
||||||
return if exit.success() {
|
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
|
/// 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.
|
/// 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.
|
/// 3. it provides convenience construction arguments to set the Command working directory and/or clear its environment.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct UCommand {
|
pub struct UCommand {
|
||||||
pub raw: Command,
|
pub raw: Command,
|
||||||
comm_string: String,
|
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::*;
|
use common::util::*;
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_env_help() {
|
fn test_env_help() {
|
||||||
assert!(new_ucmd!().arg("--help").succeeds().no_stderr().stdout.contains("Options:"));
|
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!()));
|
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]
|
#[test]
|
||||||
fn test_single_name_value_pair() {
|
fn test_single_name_value_pair() {
|
||||||
let out = new_ucmd!()
|
let out = new_ucmd!()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue