mail-archives/mono-devel-list/2013-June/040512.html

223 строки
11 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<TITLE> [Mono-dev] sub-process invocation on posix
</TITLE>
<LINK REL="Index" HREF="index.html" >
<LINK REL="made" HREF="mailto:mono-devel-list%40lists.ximian.com?Subject=Re%3A%20%5BMono-dev%5D%20sub-process%20invocation%20on%20posix&In-Reply-To=%3CCACk-zm9y8%2BqWMEakVwb8eUcwm%3DG6TZPK6zSYSsMGqRL1c2mvDg%40mail.gmail.com%3E">
<META NAME="robots" CONTENT="index,nofollow">
<style type="text/css">
pre {
white-space: pre-wrap; /* css-2.1, curent FF, Opera, Safari */
}
</style>
<META http-equiv="Content-Type" content="text/html; charset=us-ascii">
<LINK REL="Previous" HREF="040511.html">
<LINK REL="Next" HREF="040515.html">
</HEAD>
<BODY BGCOLOR="#ffffff">
<H1>[Mono-dev] sub-process invocation on posix</H1>
<B>Greg Najda</B>
<A HREF="mailto:mono-devel-list%40lists.ximian.com?Subject=Re%3A%20%5BMono-dev%5D%20sub-process%20invocation%20on%20posix&In-Reply-To=%3CCACk-zm9y8%2BqWMEakVwb8eUcwm%3DG6TZPK6zSYSsMGqRL1c2mvDg%40mail.gmail.com%3E"
TITLE="[Mono-dev] sub-process invocation on posix">gregnajda at gmail.com
</A><BR>
<I>Tue Jun 11 00:59:34 UTC 2013</I>
<P><UL>
<LI>Previous message: <A HREF="040511.html">[Mono-dev] sub-process invocation on posix
</A></li>
<LI>Next message: <A HREF="040515.html">[Mono-dev] sub-process invocation on posix
</A></li>
<LI> <B>Messages sorted by:</B>
<a href="date.html#40512">[ date ]</a>
<a href="thread.html#40512">[ thread ]</a>
<a href="subject.html#40512">[ subject ]</a>
<a href="author.html#40512">[ author ]</a>
</LI>
</UL>
<HR>
<!--beginarticle-->
<PRE>The Windows CreateProcess&lt;<A HREF="http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx">http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx</A>&gt;function
takes command line arguments as a single string. This detail
leaked into the .NET Process class. Windows programs with a
WinMain&lt;<A HREF="http://msdn.microsoft.com/en-us/library/windows/desktop/ms633559%28v=vs.85%29.aspx">http://msdn.microsoft.com/en-us/library/windows/desktop/ms633559%28v=vs.85%29.aspx</A>&gt;entry
point typically break that argument string into arguments using
CommandLineToArgvW&lt;<A HREF="http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391%28v=vs.85%29.aspx">http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391%28v=vs.85%29.aspx</A>&gt;.
With a regular &quot;main&quot; 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&lt;<A HREF="http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391%28v=vs.85%29.aspx">http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391%28v=vs.85%29.aspx</A>&gt;and
Raymond Chen's blog
post &lt;<A HREF="http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx">http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx</A>&gt;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()&lt;<A HREF="https://developer.gnome.org/glib/2.34/glib-Shell-related-Utilities.html#g-shell-parse-argv">https://developer.gnome.org/glib/2.34/glib-Shell-related-Utilities.html#g-shell-parse-argv</A>&gt;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&lt;<A HREF="https://bitbucket.org/LHCGreg/dbsc/src/c3cca47e6b190f7b6fad47c12d781e445e962acc/mydbsc/MySqlDbscEngine.cs?at=master">https://bitbucket.org/LHCGreg/dbsc/src/c3cca47e6b190f7b6fad47c12d781e445e962acc/mydbsc/MySqlDbscEngine.cs?at=master</A>&gt;.
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.
// -
<A HREF="http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs.aspx">http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs.aspx</A>
//
// Windows command line processing is funky
string escapedArg;
Regex backslashSequenceBeforeQuotes = new Regex(@&quot;(\\+)&quot;&quot;&quot;);
// Double \ sequences before &quot;s, Replace &quot; with \&quot;, double \
sequences at end
escapedArg = backslashSequenceBeforeQuotes.Replace(arg, (match)
=&gt; new string('\\', match.Groups[1].Length * 2) + &quot;\&quot;&quot;);
escapedArg = escapedArg.Replace(&quot;\&quot;&quot;, @&quot;\&quot;&quot;&quot;);
Regex backslashSequenceAtEnd = new Regex(@&quot;(\\+)$&quot;);
escapedArg = backslashSequenceAtEnd.Replace(escapedArg, (match)
=&gt; new string('\\', match.Groups[1].Length * 2));
// C:\blah\&quot;\\
// &quot;C:\blah\\\&quot;\\\\&quot;
escapedArg = &quot;\&quot;&quot; + escapedArg + &quot;\&quot;&quot;;
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 &quot; and \ with \ and enclose in quotes.
// Much simpler than Windows!
Regex backslashOrQuote = new Regex(@&quot;\\|&quot;&quot;&quot;);
return &quot;\&quot;&quot; + backslashOrQuote.Replace(arg, (match) =&gt; @&quot;\&quot; +
match.ToString()) + &quot;\&quot;&quot;;
}
Hope that helps.
- Greg
On Mon, Jun 10, 2013 at 3:46 PM, Ian Norton &lt;<A HREF="http://lists.ximian.com/mailman/listinfo/mono-devel-list">inorton at gmail.com</A>&gt; wrote:
&gt;<i> I kind of already have a thing to do that, feels a bit icky though,
</I>&gt;<i> especially as there must be some thing lower down that undoes the joined up
</I>&gt;<i> string into a char** again. :)
</I>&gt;<i>
</I>&gt;<i>
</I>&gt;<i> On 10 June 2013 16:06, Michael Hutchinson &lt;<A HREF="http://lists.ximian.com/mailman/listinfo/mono-devel-list">m.j.hutchinson at gmail.com</A>&gt;wrote:
</I>&gt;<i>
</I>&gt;&gt;<i> FWIW, you actually just need to double quote each argument and escape
</I>&gt;&gt;<i> double quotes so you can very easily write a helper to do this in a way
</I>&gt;&gt;<i> that works on both Mono and .NET:
</I>&gt;&gt;<i>
</I>&gt;&gt;<i> static Process StartProcess (string name, params string[] args)
</I>&gt;&gt;<i> {
</I>&gt;&gt;<i> string a = null;
</I>&gt;&gt;<i> if (args != null &amp;&amp; args.Length &gt; 0)
</I>&gt;&gt;<i> a = &quot;\&quot;&quot; + string.Join (args.Select (s =&gt; s.Replace (&quot;\&quot;&quot;,
</I>&gt;&gt;<i> &quot;\\\&quot;&quot;)).ToArray (), &quot;\&quot; \&quot;&quot;) + &quot;\&quot;&quot;;
</I>&gt;&gt;<i> return Process.Start (
</I>&gt;&gt;<i> new ProcessStartInfo (name, a) {
</I>&gt;&gt;<i> ShellExecute = false,
</I>&gt;&gt;<i> }
</I>&gt;&gt;<i> );
</I>&gt;&gt;<i> }
</I>&gt;&gt;<i>
</I>&gt;&gt;<i> Obviously this could be done more efficiently with a StringBuilder.
</I>&gt;&gt;<i>
</I>&gt;&gt;<i> Apologies for any errors, I'm writing this on my phone...
</I>&gt;&gt;<i>
</I>&gt;&gt;<i> - Michael
</I>&gt;&gt;<i> On Jun 6, 2013 1:18 PM, &quot;Ian Norton&quot; &lt;<A HREF="http://lists.ximian.com/mailman/listinfo/mono-devel-list">inorton at gmail.com</A>&gt; wrote:
</I>&gt;&gt;<i>
</I>&gt;&gt;&gt;<i> Hiya, I'm aware that I can use Process.Start() but I'd really really
</I>&gt;&gt;&gt;<i> like to be able to directly pass a list of strings to my child process as
</I>&gt;&gt;&gt;<i> arguments rather than having to escape shell characters and spaces etc.
</I>&gt;&gt;&gt;<i>
</I>&gt;&gt;&gt;<i> Ie, In perl or C I'd do:
</I>&gt;&gt;&gt;<i>
</I>&gt;&gt;&gt;<i> system(&quot;df&quot;,&quot;-m&quot;,&quot;/home/foo/Documents/Pictures/My Holiday&quot;);
</I>&gt;&gt;&gt;<i>
</I>&gt;&gt;&gt;<i> Where in c# I'm forced to escape the space -&gt; &quot;My\ Holiday&quot;
</I>&gt;&gt;&gt;<i>
</I>&gt;&gt;&gt;<i> Ian
</I>&gt;&gt;&gt;<i>
</I>&gt;&gt;&gt;<i> _______________________________________________
</I>&gt;&gt;&gt;<i> Mono-devel-list mailing list
</I>&gt;&gt;&gt;<i> <A HREF="http://lists.ximian.com/mailman/listinfo/mono-devel-list">Mono-devel-list at lists.ximian.com</A>
</I>&gt;&gt;&gt;<i> <A HREF="http://lists.ximian.com/mailman/listinfo/mono-devel-list">http://lists.ximian.com/mailman/listinfo/mono-devel-list</A>
</I>&gt;&gt;&gt;<i>
</I>&gt;&gt;&gt;<i>
</I>&gt;<i>
</I>&gt;<i> _______________________________________________
</I>&gt;<i> Mono-devel-list mailing list
</I>&gt;<i> <A HREF="http://lists.ximian.com/mailman/listinfo/mono-devel-list">Mono-devel-list at lists.ximian.com</A>
</I>&gt;<i> <A HREF="http://lists.ximian.com/mailman/listinfo/mono-devel-list">http://lists.ximian.com/mailman/listinfo/mono-devel-list</A>
</I>&gt;<i>
</I>&gt;<i>
</I>-------------- next part --------------
An HTML attachment was scrubbed...
URL: &lt;<A HREF="http://lists.ximian.com/pipermail/mono-devel-list/attachments/20130610/e8736642/attachment-0001.html">http://lists.ximian.com/pipermail/mono-devel-list/attachments/20130610/e8736642/attachment-0001.html</A>&gt;
</PRE>
<!--endarticle-->
<HR>
<P><UL>
<!--threads-->
<LI>Previous message: <A HREF="040511.html">[Mono-dev] sub-process invocation on posix
</A></li>
<LI>Next message: <A HREF="040515.html">[Mono-dev] sub-process invocation on posix
</A></li>
<LI> <B>Messages sorted by:</B>
<a href="date.html#40512">[ date ]</a>
<a href="thread.html#40512">[ thread ]</a>
<a href="subject.html#40512">[ subject ]</a>
<a href="author.html#40512">[ author ]</a>
</LI>
</UL>
<hr>
<a href="http://lists.ximian.com/mailman/listinfo/mono-devel-list">More information about the Mono-devel-list
mailing list</a><br>
</body></html>