ChildProcess ignoring arguments containing quotes on OS X

I encounter a strange error here:

I’m launching a child process, basically a process call followed by one argument. But the process gets executed the way it will when called with no argument. I tried both, supplying the process call and the argument with a StringVector or combined in a single String.

This is the code snippet that recreates the error:

ChildProcess childProcess;
    String cmd = "uhd_usrp_probe --args=\"addr=asdf\"";
    DBG (cmd);
    childProcess.start (cmd);
    DBG (childProcess.readAllProcessOutput());

This runs fine and the process output looks the way it should look when the command is called with NO arguments. However pasting the command string printed by the first DBG statement to the Terminal results in an error complaining about invalid arguments, as --args="add=asdf" is obviously nonsense :smiley: It normally would expect an IP address here and at the moment I can pass any valid and invalid IP address here.

However, I couldn’t recreate the error with some other process call, which makes it maybe a bit difficult for others to recreate my error. Any idea of what could be the problem with this call?

I begin to suspect that this might have something to do with the \" signs. Passing in some nonsense-argument without those characters leads to rejection of the argument just as expected.

Thanks for the report. There was a typo with a recent clean-up of the ChildProcess code. This is now fixed on develop with commit e89c4d5 and will appear on the public repo in a few minutes.

be aware that ChildProcess has some serious problems on MacOS.

Hi Fabian, thank you for the quick reaction, but however, it seems to not solve my problem :confused:

I don’t know why, but it seems to me that the " (obviously expressed as \") characters in my argument string seem to generate the problem. Any argument string in the argument Array containing a quote seems to be processed wrong or ignored. I stepped through the code a bit and can’t really find the point where this error could come from.

If I get it right, the problems described in the other thread are different to the problems I’m experiencing here?

The problems described are about the process return value being reported incorrectly by JUCE. I don’t think that is your problem.

Have you tried double escaping your parameters?

String cmd ("uhd_usrp_probe --args=\\\"addr=asdf\\\"");

What’s the exact command you are trying to run. The above command does not have any spaces so I would find it strange that it needs quotes anyway. This should do the job just as well:

    String cmd ("uhd_usrp_probe --args=addr=asdf");

Just tried double escaping, that doesn’t work.

I’ll try to explain what I’m doing. uhd_usrp_probe is a command line tool returning some information on some special 3rd-party hardware I’m using and I’m parsing the output into some JUCE-compatible data structure. The devices are connected via ethernet.

When in the terminal and just calling uhd_usrp_probe the tool will return information on whichever device on the network answers first, usually this is something completely random. When calling e.g. uhd_usrp_probe --args="addr=" it will return information on exactly the device with this IP. I know the IPs of the connected devices and they are stored into a StringArray. I want to call all connected devices, so my code looks like that:

StringArray allIPAddresses = // filled by some other function. Definitively works!
StringArray probeArgs ("uhd_usrp_probe");

for (auto &a: allIPAddresses) {

    probeArgs.set (1, "--args=\"addr=" + a + "\"");
    DBG (probeArgs[0] + " " + probeArgs[1]);
    // this prints out something like uhd_usrp_probe --args="addr=" - e.g. exactly what I want
    ChildProcess uhdUSRPProbe;
    bool success = uhdUSRPProbe.start (probeArgs);
    if (success) {
        String outputToParse = uhdUSRPProbe.readAllProcessOutput();
        DBG (outputToParse);
        // go on and parse the output

Now when looking at outputToParse it always contains information on a random device but not on the device according to the IP address passed, so I’m pretty sure that this is the result of uhd_usrp_probe being called without any argument.

The example I made up above (uhd_usrp_probe --args="addr=asdf") leads to an error when launching it in the command line, as asdf is no valid IP. However even this obviously wrong argument works fine within my code, again resulting with an output printing information on a random device. This supports my assumption that the argument is completely ignored in this case. However when removing the quotes, now the output of the child process contains an error. But trying to remove the quotes around the addr=a.b.c.d part also leads to an error, so obviously uhd_usrp_probe expects quotes around that part of the argument.

I hope that made it a bit more clear.

yes, its a different problem. However it can happen that you didn’t get any output from commands which run very shortly, maybe not at your system, but maybe at your customers system, i just thought this could be a helpful information.

As a double check for your quotes problem, you could try std::system as an alternative, redirect the output to a temporary file, read that file.

std::system indeed seems to work with the quotes in the argument! As the command invokes network communication it always takes some time to return, but however, this is a good information, thank you for that. I’m no Obj-C guy, but is there any experience with NSTask in combination with an NSPipe as shown in some examples on the internet, maybe this could be an alternative on OS X too?

However it seems that there was also an error at my side, there must have been a typo when trying to build up the string without quotes. I tried it again and now it works as expected without the quotes. But this will still limit me to pass only one flag after --args= as multiple whitespace separated flags would need the quotes, so getting an argument with quoutes working would still be a nice thing :wink:

Would it be an idea to have unit tests in place for ChildProcess to satisfy?

1 Like

I still don’t quite understand why completely removing the quotes in your example (uhd_usrp_probe --args=addr=asdf) doesn’t solve the problem. When passing the command to std::system, the command string that you provide will first be interpreted by the shell which will strip away the double quotes anyway. The double quotes simply tell the shell to interpret the text inside the quotes as a single argument to the command you are executing. Otherwise any whitespace would be a delimiter between command arguments. But as your example has no whitespace inside your quotes it should not matter if you have the quotes or not. Currently, JUCE will handle double-quotes incorrectly is some situations and therefore it would be interesting what happens when you remove the quotes.

Well, as I said in my last post, there seemed to be an error in my string when first trying to use a version without quotes. For a single argument this now works as expected without quotes.
The only reason to still use the quotes would be a whitespace-seperated argument list, which I don’t need at the moment but which could be helpful for future extensions.