Нарвался тут на интересные грабли с функциями getenv() и putenv().
С putenv() у меня уже был интересный опыт.
Часто люди пишут так:
if (getenv("A_FLAG")) {
...
}
Это работает неплохо для переменных-флагов, которые либо есть, либо нет. Значение не важно.
Что получилось у меня:
int main(...) {
putenv("GLOBAL_FLAG=1"); // Глобальное значение для всей программы.
...
system("xyz"); // Это программа должна видеть GLOBAL_FLAG=1.
...
do_stuff();
...
}
void do_stuff() {
...
if (something) {
putenv("GLOBAL_FLAG="); // Убрать переменную.
system("abc"); // А вот для этой программы флаг должен быть убран.
...
}
...
if (getenv("GLOBAL_FLAG") {
// И вот тут начиналась ерунда на разных платформах.
}
}
А корень зла тут в том, что после putenv() результат getenv() может стать либо NULL, либо "", в зависимости от платформы.
Например:
if (getenv("GLOBAL_FLAG") {
работает только на Windows и правильнее писать:
const char* p = getenv("GLOBAL_FLAG");
if (p != NULL && *p != '\0') {
...
}
И лучше сделать wrapper для getenv():
std::string GetEnv(const char* name) {
const char* v = getenv(name);
return v ? v : "";
}
И для проверки писать:
if (!GetEnv("var").empty()) {
..
}
Для теста я написал программу, которая выставляет переменную и проверяет ее значение через getenv() и через вызов дочерней программы.
#include <string>
#include <vector>
#include <iostream>
#include <cstdlib>
#ifdef WINDOWS
#define putenv _putenv
#endif
void PrintVariableViaShell(const std::string& name) {
std::cout << "Value from shell:" << std::endl;
const std::string cmd =
#ifdef WINDOWS
std::string("cmd /c echo [%") + name + "%]";
#else
std::string("/bin/sh -c \"echo [$") + name + "]\"";
#endif
std::cout << cmd << std::endl;
std::system(cmd.c_str());
}
void PrintVariableViaGetEnv(const std::string& name) {
std::cout << "Value from getenv():" << std::endl;
const char* v = std::getenv(name.c_str());
std::cout << "[" << (v ? v : "<NULL>") << "]" << std::endl;
}
void SetVariableDeleteAndPrint(const char* name_value, const bool equ) {
const std::string& name_value_s(name_value);
const std::string name = name_value_s.substr(0, name_value_s.find('='));
putenv(const_cast<char*>(name_value));
std::vector<char> delete_without_equ(name.begin(), name.end());
delete_without_equ.push_back('\0');
putenv(&delete_without_equ[0]);
std::cout << "Value after deleting WITHOUT '=':" << std::endl;
PrintVariableViaShell(name);
PrintVariableViaGetEnv(name);
std::cout << std::endl;
putenv(const_cast<char*>(name_value));
std::vector<char> delete_with_equ(name.begin(), name.end());
delete_with_equ.push_back('=');
delete_with_equ.push_back('\0');
putenv(&delete_with_equ[0]);
std::cout << "Value after deleting WITH '=': " << std::endl;
PrintVariableViaShell(name);
PrintVariableViaGetEnv(name);
}
int main(int argc, char* argv[]) {
#ifdef WINDOWS
std::cout << "WINDOWS" << std::endl;
#else
system("uname");
#endif
SetVariableDeleteAndPrint("ABC=123", true);
return 0;
}
И вот результы с разных платформ.
Linux
Linux
Value after deleting WITHOUT '=':
Value from shell:
/bin/sh -c "echo [$ABC]"
[]
Value from getenv():
[<NULL>]
Value after deleting WITH '=':
Value from shell:
/bin/sh -c "echo [$ABC]"
[]
Value from getenv():
[]
AIX
AIX
Value after deleting WITHOUT '=':
Value from shell:
/bin/sh -c "echo [$ABC]"
[123]
Value from getenv():
[123]
Value after deleting WITH '=':
Value from shell:
/bin/sh -c "echo [$ABC]"
[]
Value from getenv():
[]
SunOS
SunOS
Value after deleting WITHOUT '=':
Value from shell:
/bin/sh -c "echo [$ABC]"
[123]
Value from getenv():
[123]
Value after deleting WITH '=':
Value from shell:
/bin/sh -c "echo [$ABC]"
[]
Value from getenv():
[]
HP-UX
HP-UX
Value after deleting WITHOUT '=':
Value from shell:
/bin/sh -c "echo [$ABC]"
[123]
Value from getenv():
[123]
Value after deleting WITH '=':
Value from shell:
/bin/sh -c "echo [$ABC]"
[]
Value from getenv():
[]
WINDOWS
WINDOWS
Value after deleting WITHOUT '=':
Value from shell:
cmd /c echo [%ABC%]
[123]
Value from getenv():
[123]
Value after deleting WITH '=':
Value from shell:
cmd /c echo [%ABC%]
[%ABC%]
Value from getenv():
[<NULL>]
Только на Windows getenv() возвращает NULL после удаления. На остальных это будет пустая строка.
Забавно, на Linux можно удалять переменные через putenv("name") (без знака "="), а тогда getenv() будет возвращать NULL.
Посты по теме:
Комментариев нет:
Отправить комментарий