Expand Win env var in string

Is there a way to expand Windows environment variables into their value in Rust?

In C# I can do this: ExpandEnvVars(line.Trim())

Trying to find something similar in Rust.

Thanks

I haven't done any Windows programming so I'm not sure what exactly ExpandEnvVars does, but environment variable access in Rust is provided by functions in the std::env module.

Sorry, ExpandEnvVars will take any string and replace any environment variables in it with their value.

e.g. the string %SYSTEMROOT%\system32\binary.exe will become C:\Windows\System32\binary.exe.

I'm pretty sure that's not in the standard library, but you may be able to find a crate that has this implemented already. If I had to implement this myself, I'd use the regex crate and do something like this (untested):

pub fn expand_env_vars(s:&str)->String {
    Regex::new("%([[:word:]]*)%")
          .replace_all(s, |captures| match &captures[1] {
              "" => String::from("%"),
              varname => env::var(varname).expect("Bad Var Name")
          })
          .to_owned()
}

I'm using the Regex crate a lot in my current project already. Thanks, will give it a go.

Haven't been able to find a crate for it yet.

I'm not a regex expert but I think this would be more correct:

^%([^\/]+)%

IMHO, it only really makes sense to expand vars at the beginning of a path. And I think it should match everything up to the next % unless it's a path separator. A zero length match wouldn't make sense either so I used + instead of *.

Admittedly, I'm just guessing at the typical syntax for such things in Windows; the best choice is to go look at the documentation for the original ExpandEnvVars and match that behavior.

The reason for the zero-length match was to let %% be replaced with %; otherwise there's no reliable way to have multiple % literals in the string you're trying to expand.

Writing a forensic tool, so need to find env vars at any position. They can be used for obfuscation. Thanks though.

Ah I see. Yeah in that case you'll need something more involved.

I'm a novice at closures and Rust in general still unfortunately. Getting a type annotation needed error for "&captures[1]". Not sure how to fix that yet. I've changed the fn a little as shown below. It didn't like the "replace_all" without breaking it out.

/*
    From: https://users.rust-lang.org/t/expand-win-env-var-in-string/50320/3
*/
pub fn expand_env_vars(s:&str) -> std::io::Result<String>  {
    lazy_static! {
        static ref ENV_VAR: Regex = Regex::new("%([[:word:]]*)%").expect("Invalid Regex");
    }

    let result: String = ENV_VAR.replace_all(s, |captures| match captures[1] {
        "" => String::from("%"),
        varname => env::var(varname).expect("Bad Var Name")
    }).into();

    Ok(result)
}

It looks like this will compile:

    let result: String = ENV_VAR.replace_all(s, |c:&Captures| match &c[1] {
        "" => String::from("%"),
        varname => env::var(varname).expect("Bad Var Name")
    }).into();

(Playground)

1 Like

Cool and thanks, this will help me just understand type annotation in closures in general.