mirror of
https://github.com/uutils/coreutils
synced 2024-10-15 04:14:44 +00:00
env: add support for new '--file' option (includes testing)
.# Discussion This commit adds support for a '-f'/'--file' option which reads "KEY=VALUE" lines from a config (or ini) style text file and sets the corresponding environment key. This is modeled after the same option in the `dotenv` and `godotenv` commands. Notably, this commit does *not* add automatic loading of ".env" configuration files. The environment variables set by reading the configuration file are set prior to any unset (eg, `-u BAR`) or set (eg, `FOO=bar`) actions. Files are loaded in order with later files overwriting any overlapping environment variables, then, unset actions (in command line order) are executed, then, finally, set actions (in command line order) are executed. [1] [`dotenv`](https://github.com/bkeepers/dotenv) [2] [`godotenv`](https://github.com/joho/godotenv)
This commit is contained in:
parent
9dc31cc1ce
commit
31655fc004
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"
|
||||
|
|
46
src/env/env.rs
vendored
46
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>,
|
||||
|
@ -60,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![],
|
||||
});
|
||||
|
@ -110,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();
|
||||
|
||||
|
@ -141,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();
|
||||
|
||||
|
@ -200,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);
|
||||
}
|
||||
|
|
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"
|
|
@ -28,6 +28,36 @@ fn test_echo() {
|
|||
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…
Reference in a new issue