This shell script
#!/bin/csh
set VAR=12345
echo $VAR
will peacefully give the output 12345 at shell.
I need to use C++ to do the same in some part of the code:
string str = "12345";
retValue="set var1= "+str;
system(retValue1.c_str());
system("echo $var1");
This doesn't create a system variable var1 and echos null which is understandable as each system function would create a child process with different environment variables. So I combine them as follows using just one system function...but it echos null again.
retValue="set var1= "+str;
retValue1=retValue+";\n echo $var1";
system(retValue1.c_str());
Can someone please guide me to set up the system variable thru C++.
Thanks a lot in advance!
Look at setenv in <cstdlib>:
#include <cstdlib>
setenv("VAR", "12345", true);
You basically can't do that.
You could call putenv to change environment variables in your own process and in all future children processes, but there is no way (and this is good) to change the environment of the parent shell process.
You could have a use convention for your C++ program that e.g. it is outputting some shell commands to be sourced (or eval-ed) by the user. An example of this is ssh-agent -s.
You can use putenv().
#include <cstdlib>
...
putenv("VAR=12345");
This is very convenient, but the string is not copied by putenv. This means that if you modify it later, then you modify the environment. That's not an issue for a literal, but the way you are forming your strings, using std::string is not readily compatible with putenv().
The alternative then is to use setenv()
#include <cstdlib>
...
setenv("VAR", "12345", true);
With setenv(), copies of the input are made and you are safe to dispose of the strings after calling setenv().
The function you want is probably putenv(). You didn't specify which OS you are on, so I'll assume Linux because that's the man page I have handy:
int putenv(char *string);
The putenv() function adds or changes the value of environment
variables. The argument string is of the form name=value. If name
does not already exist in the environment, then string is added to the
environment. If name does exist, then the value of name in the
environment is changed to value. The string pointed to by string
becomes part of the environment, so altering the string changes the
environment.
IIRC, there is a putenv on win32 as well. Finally you might try looking at this question, possibly a dup
Above answers explains correctly as to how to set environment variable from a C++ program which is basically setenv()
The only other point i wanted to make is that why your approach doesn't work? The reason is, that when a process is loaded, the system command is loaded with new context - an equivalent of a new shell. Actually your environment variable is getting set but when you comeback it is lost.
Refer to this http://pubs.opengroup.org/onlinepubs/007904975/functions/setenv.html.
Infact, the setenv() sets the env variables of parent process! Which is why it works for you.
Note that the otherwise excellent CPPreference site does not seem to mention setenv among the functions available in the <cstdlib> header, only getenv. This may not be a problem though as I could use setenv by #include-ing <cstdlib> on a Centos 7 system with GCC 9.1. I suspect in most cases <cstdlib> is just a thin wrapper around <stdlib.h>.
Another small thing to note is that setenv takes C-style character arguments (also for the value of the environment variable to be set). If you use C++ strings (as you should), don't forget to convert them using their .c_str() method.
Related
Look at the code below! You will understand what I want:
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
system("set plock=24865");
system("echo %plock%"); // I know this will not work.But How to make it work?
return 0;
}
Each system() invocation creates a separate environment then destroys it when it returns to your program. This is why they can not pass information between each other.
To set an environment variable for the environment of your program use the putenv() call and later read it with the getenv() call.
The system() call inherits a copy of the environment of the program which invokes it , so at least you can set variables using putenv() and have a program invoked by system() read them.
If you expect to invoke an external program using system() and get information back from it via environment variables you can not do this easily. If this is your goal consider using fork().
Well, as you say, that won't work. What's important to understand is why: The system call spawns a child process to run the shell, and then you're setting an environment variable in that child process, which promptly terminates.
So perhaps your question is over-specific in that it asks how to do this "using system()"; short answer is you can't. (Long answer is you could set the environment variable in the registry, but that's only sensible if you intend this to be a permanent configuration change to the computer. It's not the best idea if you just mean to set a variable for use in subsequent system() calls from the same program..)
So instead you could use the SetEnvironmentVariable() function, which will set the environment variable in your current process (instead of in a child that's about to go away).
UPDATE - There is one other option, alluded to in the question's comment thread; but it assumes that you can set the variable and immediately run any/all commands that depend on it right after. In the case where you can do that, you could pack all the commands into one system call, most simply with a batch script...
Passing command-line arguments to an application in Linux just works fine with the exec* commands where you clearly pass each argument on its own. Doing so on Windows using the same functions is not an option if one wants to control the standard pipes. As those functions are based on CreateProcess() there are some clear rules on how to escape special characters like double quotes.
Sadly, this only works correctly as long as the called application retrieves its command-line arguments via main(), wmain() or CommandLineToArgvW(). However, if the called application gets those arguments via WinMain(), wWinMain(), GetCommandLineA() or GetCommandLineW() it is up to the application how to parse the command-line arguments as it gets the whole command-line rather than argument by argument.
That means a simple application named test using main() as entry point gets "abc" if called as test.exe \"abc\". Calling cmd.exe as cmd.exe /c "echo \"abc\"" will not output "abc" as expected but \"abc\".
This leads to my question:How it possible to pass command-line arguments to Windows applications in a generic way despite these quirks?
In Windows, you need to think about the command as a whole, not as a list of individual arguments. Applications are not obliged to parse the command into arguments in any particular way, or indeed at all; consider the example of the echo command, which treats the command line as a single string.
This can be a problem for runtime library developers, because it means there is no reliable way to implement a POSIX-like exec function. Some library developers take the straightforward approach, and require the programmer to provide quote marks as necessary, and some attempt to quote the arguments automatically. In the latter case it is essential to provide some method for the programmer to specify the command line as a whole, disabling any automatic quotation, even if that means a Windows-specific extension.
However, in your scenario (as described in the comments) there shouldn't be a problem. All you have to do is make sure you ask the user for a command, not for a list of arguments. Your program simply doesn't need to know how the command will be split up into arguments, if at all; understanding the command's syntax is the user's job. [NB: if you don't think this is true, you need to explain your scenario much more clearly. Provide an example of what the user might enter and how you think your application would need to process it.]
PS: since you mentioned the C library _exec functions, note that they don't work as you might be expecting. The arguments are not passed individually to the child, since that's impossible; in the Microsoft C runtime, if I remember correctly, the arguments are simply combined together into a single string, with a single space as the delimiter, so ("hello there") will be indistinguishable from ("hello", "there").
PPS: note that calling cmd.exe to parse the command introduces an additional (and much more complicated) layer of processing. Generally speaking taking that into account would still be the user's job, but you may want to be aware of it. The escape character for cmd.exe processing is the caret.
It is the C language that makes you need to use a backslash before a double quote in C code. There is no such rule for shell processing. So if you writing code to call CreateProcess and passing the literal string "abc" then you need to use backslashes because you are writing in C. But if are writing a shell script to pass invoke your app to pass "abc", e.g. the Echo example, then you don't use backslashes because there is no C code involved.
I want to make a small application that runs another application multiple times for different input parameters.
Is this already done?
Is it wrong to use system("myAp param"), for each call (of course with different param value)?
I am using kdevelop on Linux-Ubuntu.
From your comments, I understand that instead of:
system("path/to/just_testing p1 p2");
I shall use:
execl("path/to/just_testing", "path/to/just_testing", "p1", "p2", (char *) 0);
Is it true? You are saying that execl is safer than system and it is better to use?
In the non-professional field, using system() is perfectly acceptable, but be warned, people will always tell you that it's "wrong." It's not wrong, it's a way of solving your problem without getting too complicated. It's a bit sloppy, yes, but certainly is still a usable (if a bit less portable) option. The data returned by the system() call will be the return value of the application you're calling. Based on the limited information in your post, I assume that's all you're really wanting to know.
DIFFERENCES BETWEEN SYSTEM AND EXEC
system() will invoke the default command shell, which will execute the command passed as argument.
Your program will stop until the command is executed, then it'll continue.
The value you get back is not about the success of the command itself, but regards the correct opening of command shell.
A plus of system() is that it's part of the standard library.
With exec(), your process (the calling process) is replaced. Moreover you cannot invoke a script or an internal command. You could follow a commonly used technique: Differences between fork and exec
So they are quite different (for further details you could see: Difference between "system" and "exec" in Linux?).
A correct comparison is between POSIX spawn() and system(). spawn() is more complex but it allows to read the external command's return code.
SECURITY
system() (or popen()) can be a security risk since certain environment variables (like $IFS / $PATH) can be modified so that your program will execute external programs you never intended it to (i.e. a command is specified without a path name and the command processor path name resolution mechanism is accessible to an attacker).
Also the system() function can result in exploitable vulnerabilities:
when passing an unsanitized or improperly sanitized command string originating from a tainted source;
if a relative path to an executable is specified and control over the current working directory is accessible to an attacker;
if the specified executable program can be spoofed by an attacker.
For further details: ENV33-C. Do not call system()
Anyway... I like Somberdon's answer.
For C/C++ there seems to be no portable function to get the user name in Linux/Posix and Windows.
What would be the least cumbersome and yet robust portable code to achieve this ?
In Linux the USER environment variable seems always to be defined, whereas Windows seems to define the USERNAME variable. Relying on getenv one could avoid including windows.h and minimize preprocessor statements:
char * user_name = getenv("USER");
if (!user_name) {
user_name = getenv("USERNAME");
}
But is this approach halfway robust ? Or am I ignorant to another solution ? And I was also ignorant towards iOS ...
Using environment variables to find the username is very unreliable:
I expanded your code to this:
#include <iostream>
int main()
{
char * user_name = getenv("USER");
std::cout << user_name << std::endl;
}
and did this:
$ whoami
MatsP
$ g++ -Wall -std=c++0x getenv.cpp
$ ./a.out
MatsP
$ export USER=DonaldDuck
$ ./a.out
DonaldDuck
So, I would definitely suggest that if the username is of any importance to your code, then you should use a system-specific function to get the username. If it's not important to your code, ask at install time or some such, and don't try to use any system function to fetch the username.
(In answer to your actual question, yes, I would think that an iOS has the same functionality of a USER environment variable - although I'm not sure there is a meaningful user on an iPhone).
There is no portable solution.
Under Linux (and Unix in general), a "user" is a number, and can
have several different names. Use getuid to get the id, then
getpwuid to get one of the names. Or use getlogin to get
the name the user used to login (but this will only work if the
process has a controlling terminal). If you want to get all
of the names the user is known under, you'll have to iterate
using getpwent, checking the pw_uid field for each entry
returned.
The use of an environment variable is not guaranteed. In many
contexts, the environment variable won't be set, and even if it
is, it's not guaranteed to be correct.
Windows has a function GetUserName; I don't know too much
about it, however.
The user concept is related to the operating system or even system, not the language.
As there are a myriad of different systems around, the only feasable approach is to limit yourself to a list of systems you want to support.
There exists software that can help you to achieve portability, like the GNU autotools for Unix like systems (and to a certain degree Windows).
You are right - there is no portable way to do it.
There are two solutions for that: use proper set of ifdef's to use one code on window, and another on linux.
the other way is to do it with factory which would return you an abstract object or pointer to function which would be system specific. Factory should detect system and return proper function/class
How do I find the 'temp' directory in Linux? I am writing a platform neutral C++ function that returns the temp directory. In Mac and Windows, there is an API that returns these results. In Linux, I'm stumped.
Check following variables:
The environment variable TMPDIR
The value of the P_tmpdir macro
If all fails try to use the directory /tmp.
You can also use tempnam function to generate a unique temporary file name.
Edit: Fair point from the commenter. tmpnam isn't a good choice these days; use mktemp/mkstemp instead.
Historical answer: Be POSIX compliant, and use tmpnam (which will give you a full filename in a temporary location).
Use the value of the $TMPDIR environment variable, and if that doesn't exist, use /tmp.
The accepted sequence, specifically from a GNU standpoint, is:
Check the environmental variable TMPDIR (getenv("TMPDIR")) only if
the program is not running as SUID/SGID (issetugid() == 0)
Otherwise use P_tmpdir if it is defined and is valid
and finally, should those fail, use _PATH_TMP available from paths.h
If you are adding an extension or module, check to see if the core provides a function for this purpose. For example, PHP exports php_get_temporary_directory() from main/php_open_temporary_file.h.
In standard c, you could try: P_tmpdir