expand file names that have environment variables in their path
What's the best way to expand
${MyPath}/filename.txt to /home/user/filename.txt
or
%MyPath%/filename.txt to c:\Documents and settings\user\filename.txt
with out traversing the path string looking for environement variables directly? I see that wxWidgets has a wxExpandEnvVars function. I can't use wxWidgets in this case, so I was hoping to find a boost::filesystem equivalent or similar. I am only using the home directory as an example, I am looking for general purpose path expansion.
For UNIX (or at least POSIX) systems, have a look at wordexp:
#include <iostream>
#include <wordexp.h>
using namespace std;
int main() {
wordexp_t p;
char** w;
wordexp( "$HOME/bin", &p, 0 );
w = p.we_wordv;
for (size_t i=0; i<p.we_wordc;i++ ) cout << w[i] << endl;
wordfree( &p );
return 0;
}
It seems it will even do glob-like expansions (which may or may not be useful for your particular situation).
On Windows, you can use ExpandEnvironmentStrings
. Not sure about a Unix equivalent yet.
If you have the luxury of using C++11, then regular expressions are quite handy. I wrote a version for updating in place and a declarative version.
#include <string>
#include <regex>
// Update the input string.
void autoExpandEnvironmentVariables( std::string & text ) {
static std::regex env( "\\$\\{([^}]+)\\}" );
std::smatch match;
while ( std::regex_search( text, match, env ) ) {
const char * s = getenv( match[1].str().c_str() );
const std::string var( s == NULL ? "" : s );
text.replace( match[0].first, match[0].second, var );
}
}
// Leave input alone and return new string.
std::string expandEnvironmentVariables( const std::string & input ) {
std::string text = input;
autoExpandEnvironmentVariables( text );
return text;
}
An advantage of this approach is that it can be adapted easily to cope with syntactic variations and deal with wide strings too. (Compiled and tested using Clang on OS X with the flag -std=c++0x)
Simple and portable:
#include <cstdlib>
#include <string>
static std::string expand_environment_variables( const std::string &s ) {
if( s.find( "${" ) == std::string::npos ) return s;
std::string pre = s.substr( 0, s.find( "${" ) );
std::string post = s.substr( s.find( "${" ) + 2 );
if( post.find( '}' ) == std::string::npos ) return s;
std::string variable = post.substr( 0, post.find( '}' ) );
std::string value = "";
post = post.substr( post.find( '}' ) + 1 );
const char *v = getenv( variable.c_str() );
if( v != NULL ) value = std::string( v );
return expand_environment_variables( pre + value + post );
}
expand_environment_variables( "${HOME}/.myconfigfile" );
yields /home/joe/.myconfigfile