[Mono-dev] sub-process invocation on posix
Ian Norton
inorton at gmail.com
Tue Jun 11 20:29:42 UTC 2013
Excellent Thanks!
On 11 June 2013 01:59, Greg Najda <gregnajda at gmail.com> wrote:
> The Windows CreateProcess<http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx>function takes command line arguments as a single string. This detail
> leaked into the .NET Process class. Windows programs with a WinMain<http://msdn.microsoft.com/en-us/library/windows/desktop/ms633559%28v=vs.85%29.aspx>entry point typically break that argument string into arguments using
> CommandLineToArgvW<http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391%28v=vs.85%29.aspx>.
> With a regular "main" entry point, the C runtime does that for you.
> Unfortunately there is no ArgvToCommandLine function, which is a shame
> because CommandLineToArgvW has pretty funky rules for quotes and
> backslashes. See the docs for CommandLineToArgvW<http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391%28v=vs.85%29.aspx>and Raymond Chen's blog
> post<http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx>for info. Simply enclosing in quotes and putting a backslash before quotes
> and backslashes is not good enough for Windows.
>
> I was curious about this myself a week or two ago because I had to pass
> some dynamic arguments to a process so I dove into the Mono source. On
> Windows Mono passes the argument string as is to CreateProcess. On Unix
> platforms Mono uses the GNOME g_shell_parse_argv()<https://developer.gnome.org/glib/2.34/glib-Shell-related-Utilities.html#g-shell-parse-argv>function to convert the arg string into an argv before starting the process.
>
> Feel free to use the following code taken from a personal project of mine<https://bitbucket.org/LHCGreg/dbsc/src/c3cca47e6b190f7b6fad47c12d781e445e962acc/mydbsc/MySqlDbscEngine.cs?at=master>.
> It passes the unit tests I threw it.
>
> private string QuoteCommandLineArg(string arg)
> {
> if (Environment.OSVersion.Platform == PlatformID.Unix ||
> Environment.OSVersion.Platform == PlatformID.MacOSX)
> {
> return QuoteCommandLineArgUnix(arg);
> }
> else
> {
> return QuoteCommandLineArgWindows(arg);
> }
> }
>
> internal static string QuoteCommandLineArgWindows(string arg)
> {
> // If a double quotation mark follows two or an even number of
> backslashes,
> // each proceeding backslash pair is replaced with one
> backslash and the double quotation mark is removed.
> // If a double quotation mark follows an odd number of
> backslashes, including just one,
> // each preceding pair is replaced with one backslash and the
> remaining backslash is removed;
> // however, in this case the double quotation mark is not
> removed.
> // -
> http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs.aspx
> //
> // Windows command line processing is funky
>
> string escapedArg;
> Regex backslashSequenceBeforeQuotes = new Regex(@"(\\+)""");
> // Double \ sequences before "s, Replace " with \", double \
> sequences at end
> escapedArg = backslashSequenceBeforeQuotes.Replace(arg,
> (match) => new string('\\', match.Groups[1].Length * 2) + "\"");
> escapedArg = escapedArg.Replace("\"", @"\""");
> Regex backslashSequenceAtEnd = new Regex(@"(\\+)$");
> escapedArg = backslashSequenceAtEnd.Replace(escapedArg,
> (match) => new string('\\', match.Groups[1].Length * 2));
> // C:\blah\"\\
> // "C:\blah\\\"\\\\"
> escapedArg = "\"" + escapedArg + "\"";
> return escapedArg;
> }
>
> internal static string QuoteCommandLineArgUnix(string arg)
> {
> // Mono uses the GNOME g_shell_parse_argv() function to
> convert the arg string into an argv
> // Just prepend " and \ with \ and enclose in quotes.
> // Much simpler than Windows!
>
> Regex backslashOrQuote = new Regex(@"\\|""");
> return "\"" + backslashOrQuote.Replace(arg, (match) => @"\" +
> match.ToString()) + "\"";
> }
>
>
> Hope that helps.
>
> - Greg
>
>
>
> On Mon, Jun 10, 2013 at 3:46 PM, Ian Norton <inorton at gmail.com> wrote:
>
>> I kind of already have a thing to do that, feels a bit icky though,
>> especially as there must be some thing lower down that undoes the joined up
>> string into a char** again. :)
>>
>>
>> On 10 June 2013 16:06, Michael Hutchinson <m.j.hutchinson at gmail.com>wrote:
>>
>>> FWIW, you actually just need to double quote each argument and escape
>>> double quotes so you can very easily write a helper to do this in a way
>>> that works on both Mono and .NET:
>>>
>>> static Process StartProcess (string name, params string[] args)
>>> {
>>> string a = null;
>>> if (args != null && args.Length > 0)
>>> a = "\"" + string.Join (args.Select (s => s.Replace ("\"",
>>> "\\\"")).ToArray (), "\" \"") + "\"";
>>> return Process.Start (
>>> new ProcessStartInfo (name, a) {
>>> ShellExecute = false,
>>> }
>>> );
>>> }
>>>
>>> Obviously this could be done more efficiently with a StringBuilder.
>>>
>>> Apologies for any errors, I'm writing this on my phone...
>>>
>>> - Michael
>>> On Jun 6, 2013 1:18 PM, "Ian Norton" <inorton at gmail.com> wrote:
>>>
>>>> Hiya, I'm aware that I can use Process.Start() but I'd really really
>>>> like to be able to directly pass a list of strings to my child process as
>>>> arguments rather than having to escape shell characters and spaces etc.
>>>>
>>>> Ie, In perl or C I'd do:
>>>>
>>>> system("df","-m","/home/foo/Documents/Pictures/My Holiday");
>>>>
>>>> Where in c# I'm forced to escape the space -> "My\ Holiday"
>>>>
>>>> Ian
>>>>
>>>> _______________________________________________
>>>> Mono-devel-list mailing list
>>>> Mono-devel-list at lists.ximian.com
>>>> http://lists.ximian.com/mailman/listinfo/mono-devel-list
>>>>
>>>>
>>
>> _______________________________________________
>> Mono-devel-list mailing list
>> Mono-devel-list at lists.ximian.com
>> http://lists.ximian.com/mailman/listinfo/mono-devel-list
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ximian.com/pipermail/mono-devel-list/attachments/20130611/50dedc66/attachment.html>
More information about the Mono-devel-list
mailing list