Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expand environment variables in text

Tags:

java

regex

I'm trying to write a function to perform substitutions of environment variables in java. So if I had a string that looked like this:

User ${USERNAME}'s APPDATA path is ${APPDATA}.

I want the result to be:

User msmith's APPDATA path is C:\Users\msmith\AppData\Roaming.

So far my broken implementation looks like this:

public static String expandEnvVars(String text) {        
    Map<String, String> envMap = System.getenv();
    String pattern = "\\$\\{([A-Za-z0-9]+)\\}";
    Pattern expr = Pattern.compile(pattern);
    Matcher matcher = expr.matcher(text);
    if (matcher.matches()) {
        for (int i = 1; i <= matcher.groupCount(); i++) {
            String envValue = envMap.get(matcher.group(i).toUpperCase());
            if (envValue == null) {
                envValue = "";
            } else {
                envValue = envValue.replace("\\", "\\\\");
            }
            Pattern subexpr = Pattern.compile("\\$\\{" + matcher.group(i) + "\\}");
            text = subexpr.matcher(text).replaceAll(envValue);
        }
    }
    return text;
}

Using the above sample text, matcher.matches() returns false. However if my sample text, is ${APPDATA} it works.

Can anyone help?

like image 542
Michael Smith Avatar asked Jan 20 '11 21:01

Michael Smith


3 Answers

You don't want to use matches(). Matches will try to match the entire input string.

Attempts to match the entire region against the pattern.

What you want is while(matcher.find()) {. That will match each instance of your pattern. Check out the documentation for find().

Within each match, group 0 will be the entire matched string (${appdata}) and group 1 will be the appdata part.

Your end result should look something like:

String pattern = "\\$\\{([A-Za-z0-9]+)\\}";
Pattern expr = Pattern.compile(pattern);
Matcher matcher = expr.matcher(text);
while (matcher.find()) {
    String envValue = envMap.get(matcher.group(1).toUpperCase());
    if (envValue == null) {
        envValue = "";
    } else {
        envValue = envValue.replace("\\", "\\\\");
    }
    Pattern subexpr = Pattern.compile(Pattern.quote(matcher.group(0)));
    text = subexpr.matcher(text).replaceAll(envValue);
}
like image 69
jjnguy Avatar answered Oct 22 '22 10:10

jjnguy


If you don't want to write the code for yourself, the Apache Commons Lang library has a class called StrSubstitutor. It does exactly this.

like image 40
rfeak Avatar answered Oct 22 '22 11:10

rfeak


The following alternative has the desired effect without resorting to a library:

  • reads the map of environment variables once at startup
  • on calling expandEnvVars() takes the text with potential placeholders as an argument
  • the method then goes through the map of environment variables, one entry at at time, fetches the entry's key and value
  • and tries to replace any occurrence of ${<key>} in the text with <value>, thereby expanding the placeholders to their current values in the environment
    private static Map<String, String> envMap = System.getenv();        
    public static String expandEnvVars(String text) {        
        for (Entry<String, String> entry : envMap.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            text = text.replaceAll("\\$\\{" + key + "\\}", value);
        }
        return text;
    }
like image 30
user2161699 Avatar answered Oct 22 '22 12:10

user2161699