How to call popen() with a pathname containing spaces under Windows in C/C++?
Asked Answered
A

5

10

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() {
    outputPointer = popen(RUN_COMMAND, "r");
    ...
}

But I can't get it working. I think it's a quoting nightmare ...

Archicarp answered 12/10, 2009 at 21:39 Comment(1)
You asked this around the time I made the first few public releases of a programming language I started working on, almost six years ago! I came across and solved this issue in the Windows port of a library function of that language, which is now in its 105-th public release. Basically I'm emulating the opening of a process with an argument list, but using the popen function in MinGW (which maps to the Microsoft C Run-Time Library _popen) which means I have to carefully encode the process name and argument strings. So, there is your answer.Tresatrescha
E
12

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

Erumpent answered 6/5, 2017 at 16:20 Comment(1)
Its seems silly that this would work... but when I tried it all of my quoting suddenly started working...Tess
T
5

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.

Tresatrescha answered 16/4, 2015 at 3:44 Comment(0)
S
4

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"
Sielen answered 12/10, 2009 at 21:59 Comment(1)
A program compiled by MinGW does not have a _popen that passes parameters to sh -c; it's linked to an internal Windows library called msvcrt.dll whose _popen certainly doesn't do any such thing. MinGW's MSYS library (used by the toolchain itself) probably has a popen that calls sh. The Cygwin DLL does that, and MSYS is a derivative of Cygwin. When you build programs with MinGW, they are not linked with MSYS though.Tresatrescha
B
1

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);
}
Bibber answered 27/12, 2016 at 2:0 Comment(0)
T
-1

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");
    ...
}
Thievery answered 12/10, 2009 at 21:52 Comment(1)
Bear in mind this is MinGW, though, so I don't know that popen passes the command to /bin/sh. Could it be passing it to some other more Windows-y and less POSIX-y shell instead? One which will interpret those backslashes as path separators rather than escapes, and which will have its own quoting rules.Ellen

© 2022 - 2024 — McMap. All rights reserved.