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.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.