2009-12-14 5 views
18

Cuál es la mejor manera de ampliarampliar nombres de archivos que tienen las variables de entorno en su camino

${MyPath}/filename.txt to /home/user/filename.txt 

o

%MyPath%/filename.txt to c:\Documents and settings\user\filename.txt 

con a recorrer la cadena camino en busca de las variables de entorno de forma directa? Veo que wxWidgets tiene una función wxExpandEnvVars. No puedo usar wxWidgets en este caso, así que esperaba encontrar un boost :: filesystem equivalent o similar. Solo estoy usando el directorio de inicio como ejemplo, estoy buscando expansión de ruta de propósito general.

Respuesta

16

En Windows, puede usar ExpandEnvironmentStrings. No estoy seguro acerca de un equivalente de Unix todavía.

+0

Gracias Rob. Esto me lleva a mitad de camino allí. Supongo que examinaré el método de análisis para casos que no sean Windows. – Dan

19

para sistemas Unix (o al menos POSIX), echar un vistazo a 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; 
} 

Parece que incluso hará expansiones glob-como (que pueden o no ser útil para su situación particular) .

+0

Para mí, esta es la otra mitad. Esta solución con la anterior funciona en Posix y Window. – xryl669

+1

Rechazaría esto tres veces si pudiera. Gran respuesta utilizando las instalaciones proporcionadas por el sistema que son ciertamente menos propensas a errores que las expresiones regulares. –

3

Dentro del lenguaje C/C++, esto es lo que debo hacer para resolver las variables del entorno en Unix. El puntero fs_parm contendría la especificación de archivos (o texto) de las variables de entorno posibles para expandir. El espacio al que apunta wrkSpc debe ser MAX_PATH + 60 caracteres de longitud. Las comillas dobles en la cadena de eco son para evitar que las comodines se procesen. La mayoría de las shells predeterminadas deberían poder manejar esto.


    FILE *fp1; 

    sprintf(wrkSpc, "echo \"%s\" 2>/dev/null", fs_parm); 
    if ((fp1 = popen(wrkSpc, "r")) == NULL || /* do echo cmd  */ 
     fgets(wrkSpc, MAX_NAME, fp1) == NULL)/* Get echo results */ 
    {      /* open/get pipe failed    */ 
    pclose(fp1);   /* close pipe      */ 
    return (P_ERROR);  /* pipe function failed    */ 
    } 
    pclose(fp1);    /* close pipe      */ 
    wrkSpc[strlen(wrkSpc)-1] = '\0';/* remove newline   */ 

Para MS Windows, use la función ExpandEnvironmentStrings().

1

Esto es lo que yo uso:

const unsigned short expandEnvVars(std::string& original) 
{ 
    const boost::regex envscan("%([0-9A-Za-z\\/]*)%"); 
    const boost::sregex_iterator end; 
    typedef std::list<std::tuple<const std::string,const std::string>> t2StrLst; 
    t2StrLst replacements; 
    for (boost::sregex_iterator rit(original.begin(), original.end(), envscan); rit != end; ++rit) 
     replacements.push_back(std::make_pair((*rit)[0],(*rit)[1])); 
    unsigned short cnt = 0; 
    for (t2StrLst::const_iterator lit = replacements.begin(); lit != replacements.end(); ++lit) 
    { 
     const char* expanded = std::getenv(std::get<1>(*lit).c_str()); 
     if (expanded == NULL) 
      continue; 
     boost::replace_all(original, std::get<0>(*lit), expanded); 
     cnt++; 
    } 
    return cnt; 
} 
1

Como la cuestión se etiqueta "wxWidgets", puede utilizar la función wxExpandEnvVars() utilizado por wxConfig para su expansión variable de entorno. Desafortunadamente, la función no está documentada, pero básicamente hace lo que usted cree que debería hacer y expande cualquier aparición de $VAR, $(VAR) o ${VAR} en todas las plataformas y también de %VAR% en Windows solamente.

-1

Como la pregunta está etiquetada C++, puede usar la norma getenv, con un toque de C++ 11 regex estándar para reemplazar la variable por su valor.

6

simple y portátil:

#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 *v = getenv(variable.c_str()); 
    if(v != NULL) value = std::string(v); 

    return expand_environment_variables(pre + value + post); 
} 

expand_environment_variables("${HOME}/.myconfigfile"); produce /home/joe/.myconfigfile

+0

Tenga en cuenta que esto solo expande una sola variable de entorno. Lo cual está bien para el ejemplo. Si necesita expandir múltiples variables de entorno en una cadena, llama a expane_environment_variables recursivamente en la publicación. – tbleher

+1

Nota: llama 'getenv (variable.c_str())' dos veces para obtener la misma var, mejor para almacenar el resultado. – vladon

0

Usando Qt, esto funciona para mí:

#include <QString> 
#include <QRegExp> 

QString expand_environment_variables(QString s) 
{ 
    QString r(s); 
    QRegExp env_var("\\$([A-Za-z0-9_]+)"); 
    int i; 

    while((i = env_var.indexIn(r)) != -1) { 
     QByteArray value(qgetenv(env_var.cap(1).toLatin1().data())); 
     if(value.size() > 0) { 
      r.remove(i, env_var.matchedLength()); 
      r.insert(i, value); 
     } else 
      break; 
    } 
    return r; 
} 

expand_environment_variables (QString ("$ HOME/.myconfigfile")) ; cede /home/martin/.myconfigfile (También funciona con expansiones anidadas)

5

Si tiene el lujo de usar C++ 11, las expresiones regulares son bastante útiles. Escribí una versión para actualizar en el lugar y una versión declarativa.

#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; 
} 

Una ventaja de este enfoque es que puede ser fácilmente adaptado para hacer frente a las variaciones sintácticas y hacer frente a las cadenas de ancho también. (Compilado y probado usando Clang en OS X con la bandera -std = C++ 0x)

+0

g ++ 4.9.3 (Ubuntu) no se puede compilar, es algo relacionado con la conversión entre iterador y const_iterator. Tengo que cambiar text.replace (coincide con [0] .first, match [0] .segundo, var); to text.replace (match.position (0), match.length (0), var); – fchen

Cuestiones relacionadas