Important: see update at the end for POSIX systems.
My opinion is that you should use the APIs/syscalls provided by the various platforms you wish to support, or use some kind of abstraction layer (the Boost.Process library, mentioned by Noah Roberts, may be an idea) to avoid dealing with platform-specific details.
I strongly disagree with using the system
function because it isn't intended to start a process you specify, but instead it's supposed to pass the string you specified to the "system default shell" or "command processor" (if any). This has several drawbacks:
- resource wastage; instead of a process now (usually) you are spawning two, one of which (the shell) is useless for your final objective (starting the process you want). This is usually negligible, but may be noticeable on systems where processes aren't lightweight objects (Windows) if they are running low on resources.
- useless confusion; several security suites I've dealt with warn every time an unknown/untrusted process starts a new process; instead of just displaying a warning, now the security suite will display two of them (and you're making the first one quite unclear);
- unpredictability of the result; the platform-agnostic
system
's documentation could be replaced without much loss with "undefined behavior" - and actually it is quite like that. Why do I say this? Because:
- first of all, there's not even a guarantee that
system
has some meaning on the current platform, as there could be no "default shell" at all. But this is an extreme case that isn't usually a problem - and that can be also caught quite easily (if(system(NULL)==0)
there's no shell); the real problem is that
- in general, you don't have idea about what shell is the "default shell", and how it parses its input; on Linux it will
usually be /bin/sh
update: actually, this is mandated by POSIX, see below, on Windows it may be command.com
as well as cmd.exe
, on another OS it will be still another thing. So, you aren't sure about, e.g., how to escape spaces in the path, or if you should quote the path; heck, you don't even know if such shell requires some special command to start executables!
- More fun: you don't even know if the call is actually blocking: you know that by the time
system
will return the shell will be terminated, but you don't know if the shell will wait for the spawned process to end; concrete example: cmd.exe
doesn't wait for GUI executables to end before returning, while on Linux GUI executables are executables like all the others and don't have such special treatment. In this case you'll have to create a special case for Windows, and make a command string like start /wait youexecutable.exe
- hoping that the version of the interpreter still (or yet, depending on the version of Windows) supports that syntax. And IIRC start
has different options on Windows 9x and Windows NT family, so you won't even be sure with that.
- It's not enough: you aren't even sure if the application has been started: the
system
return value is relative to the command interpreter return code. As far as system
is concerned, if a shell is started the call succeeded, and there ends what system
considers an error.
- Then you're left with the error code of the shell - about which, again, we don't know anything. Maybe it's a carbon-copy of the error code of the last executed command; maybe it is an error code relative just to the shell (e.g., 1 = last command executed, 0 = last command was invalid), maybe it's 42. Who knows?
Since in a good application you'll want, at least, to know if the call is blocking/nonblocking, to get a meaningful exit code (the one actually returned by the application you started), to be sure if the application has been started, to have meaningful error codes in case things went wrong, system
most probably doesn't suit your needs; to earn any of these guarantees, you have to go with platform-specific hacks or with non guaranteed assumptions, wasting all the cross-platform "compatibility" of system
.
So, I'll state it again: use the system calls provided by the various platforms (e.g., fork+exec on POSIX, CreateProcess on Windows), which specify exactly what they are guaranteed to do, or go with third party abstraction code; the system
way is definitely not good.
Update: since when I wrote this answer, I learned that on POSIX systems system
is specified way better - in particular, it's mandated that it will execute the commands with /bin/sh -c command
, blocking until the termination of the shell process.
sh
behavior, in turn, is mandated in several ways by POSIX; thus, on POSIX systems, some of the disadvantages listed under "unpredictability of the result" no longer apply:
- the default shell is specified, so, as long as you use just
sh
stuff guaranteed by POSIX (e.g. no bash
isms), you are safe;
- the call is blocking;
- if your command is well-formed, the shell itself doesn't encounter problems,
waitpid
succedes, ..., you should get a copy of the error code of the executed program
So, if you run on POSIX, the situation is way less tragic; if, instead, you have to be portable, keep avoiding system
.