How to call popen() with a pathname containing spaces under Windows in C/C++?
I'm trying to call popen() using mingw like this:
#define RUN_COMMAND "\"C:\\Program Files\\CA\\BrightStor ARCserve Backup\\ca_qmgr.exe\" \"-list\""
int main() {
outputPointe开发者_Python百科r = popen(RUN_COMMAND, "r");
...
}
But I can't get it working. I think it's a quoting nightmare ...
It turns out the popen function strips the quotation marks around the whole thing, eg. "C:/Program Files"
becomes C:/Program Files
and "Ab cd.exe" "ef gh"
becomes ab cd" "ef gh
. You can get around this by just adding quotation marks around the whole thing, eg. ""ab cd.exe" "ef gh""
which becomes "ab cd.exe" "ef gh"
as intended.
This solution was inspired by reply from Kaz
This problem turns out to be solvable. (Though, as with most things in Windows, not 100%).
The popen
function opens a system command, which means that it is almost certainly just taking the string argument and interpolating it into cmd.exe /c <here>
.
After some interactive experimentation with cmd.exe /c
at the Windows command prompt, we hit upon a way to run a program which has spaces in its pathname.
Naive attempt with quotes, nope:
C:\>cmd /c "foo bar"
'foo' is not recognized as an internal or external command,
operable program or batch file.
Can we escape the quotes with the circumflex?
C:\>cmd /c ^"foo bar^"
'foo' is not recognized [...]
How about the space?
C:\>cmd /c foo^ bar
'foo' is not recognized [...]
How about quoting just the space?
C:\>cmd /c foo" "bar
'foo" "bar' is not recognized as an internal or external command,
operable program or batch file.
Aha! This is promising! Is it actually trying to run foo bar
? Let's see:
C:\>copy con "foo bar.bat"
echo i am foo bar
^Z
1 file(s) copied.
C:\>cmd /c foo" "bar
C:\>echo i am foo bar
i am foo bar
Aha! Our batch file with a space in its name was run!
And so, now I'm trying the following in Visual Studio 2008:
#include <stdio.h>
int main()
{
FILE *in = _popen("C:\\Program\" \"Files\\Internet\" \"Explorer\\iexplore.exe", "r");
if (in) {
char line[200];
printf("successfully opened!\n");
if (fgets(line, 200, in))
fputs(line, stdout);
_pclose(in);
}
return 0;
}
It starts Internet Explorer just fine, then blocks in the fgets
until you exit the browser (and fails to read anything since there is no output).
It does work in MinGW, which uses the Microsoft C Run-Time Library; in fact, I came up with this solution to fix a bug in the library of a programming language which is ported to Windows using MinGW.
Caveats:
This escaping trick won't escape leading spaces. It does seem to work for trailing spaces. Neither is likely a problem; we don't often see pathnames that begin or end with spaces, let alone executable path names.
I also can't find a way to escape embedded double quotes that might occur as part of a path, while at the same time handling spaces. People should probably think twice before creating something like C:\Program Files\Bob's so-called "editor"\bedit.exe
.
Take a look at What is the equivalent to posix popen in the win32 api. I ditched my attempt to use _popen()
under Windows a long time ago. The pure WIN32 solution performs much more reliably.
If mingw is passing your parameters to sh -c
instead of cmd.exe /c
, then you probably want to change those backslashes to forward slashes. Something like the following should do the trick.
"\"C:/Program Files/CA/BrightStor ARCserve Backup/ca_qmgr.exe\" -list"
I was just faced with this problem, and tried Kaz's solution without success. The way I solved it was simply to change directory, execute _popen without the path to the executable, then change back to the original directory.
char cwd[MAX_PATH];
_getcwd(cwd, MAX_PATH);
if (_chdir("C:\\Program Files\\CA\\BrightStor ARCserve Backup") >= 0)
{
outputPointer = _popen("ca_qmgr.exe \"-list\"", "r");
_chdir(cwd);
}
According to the man page for popen:
The command argument is a pointer to a null-terminated string containing a shell command line. This command is passed to /bin/sh using the -c flag; interpretation, if any, is performed by the shell.
So what you want is to escape the string for sh
to parse.
You do not need to escape the -list
argument, and you can try to escape the spaces instead of quoting the whole program name. I do not have a windows system here to test with but I would suggest
#define RUN_COMMAND "C:\\Program Files\\CA\\BrightStor\\ ARCserve\\ Backup\\ca_qmgr.exe -list"
int main() {
outputPointer = popen(RUN_COMMAND, "r");
...
}
精彩评论