Expand environment variables in text
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;
}
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.
Based on the accepted answer but without nested pattern replacement.
Supports also default value and underscores in env-name: ${env-var[:default]}
static String substituteEnvVars(String text) {
StringBuffer sb = new StringBuffer();
String pattern = "\\$\\{([A-Za-z0-9_]+)(?::([^\\}]*))?\\}";
Pattern expr = Pattern.compile(pattern);
Matcher matcher = expr.matcher(text);
while (matcher.find()) {
final String varname = matcher.group(1);
String envValue = System.getenv(varname);
if (envValue == null) {
envValue = matcher.group(2);
if (envValue == null)
envValue = "";
}
matcher.appendReplacement(sb, envValue);
}
matcher.appendTail(sb);
return sb.toString();
}
In additon variables are exactly substituted once. Text "${VAR}${X}" with VAR=${X} and X=x will be return "${X}x" not "xx".
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);
}