2010-06-02 12 views
7

Ich habe eine C# -Kommandozeilenanwendung, die ich in Windows und unter Mono in Unix ausführen muss. Irgendwann möchte ich einen Subprozess starten, der eine Menge beliebiger Parameter enthält, die über die Befehlszeile übergeben werden. Zum Beispiel:Wie starte ich einen Subprozess in C# mit einem Argv? (Oder wandeln Sie agrv in eine legale Arg-Zeichenfolge um)

Usage: mycommandline [-args] -- [arbitrary program] 

Leider nimmt System.Diagnostics.ProcessStartInfo nur eine Zeichenfolge für Argumente. Dies ist ein Problem für Befehle wie:

./my_commandline myarg1 myarg2 -- grep "a b c" foo.txt 

In diesem Fall argv wie folgt aussieht:

argv = {"my_commandline", "myarg1", "myarg2", "--", "grep", "a b c", "foo.txt"} 

Beachten Sie, dass die Anführungszeichen um „abc“ durch die Schale entfernt, so, wenn ich einfach die Argumente verketten um den aRG-String für Process zu erstellen, erhalte ich:

args = "my_commandline myarg1 myarg2 -- grep a b c foo.txt" 

welche nicht das, was ich will.

Gibt es eine einfache Möglichkeit, entweder einen argv zum Unterprozessstart unter C# OR zu übergeben, um einen beliebigen argv in einen String zu konvertieren, der für Windows und Linux Shell zulässig ist?

Jede Hilfe würde sehr geschätzt werden.

+1

Nur ein Hinweis: C# unter Windows hat dieses Problem nicht, da Windows technisch kein argv hat. Die gesamte Befehlszeile wird an den Prozess übergeben, und die Aufteilung der Argumente ist die Aufgabe des neuen Prozesses, nicht des Betriebssystems.Unix verfolgt den umgekehrten Weg: Das Betriebssystem ist dafür verantwortlich, ein Array von Strings zu übergeben, so dass Dinge wie die Platzhaltererweiterung vom aufrufenden Prozess gehandhabt werden können. –

+0

@DanielPryden das ist nicht wahr. Windows-Programme haben eine argv-Liste wie POSIX-Dateien. .Net versteckt diese Liste nur wenig hilfreich. Eher irritierend, wenn Sie mich fragen. – IanNorton

+0

@IanNorton: Im Gegensatz dazu nimmt 'CreateProcess' nur eine einzige Argument-Zeichenfolge. Die Microsoft C-Laufzeit (MSVCRT) analysiert diese Zeichenfolge und stellt sie als argv zur Verfügung, aber für die Verwendung der CRT-Implementierung sind keine Programme erforderlich, und die Analyse, die die CRT durchführt, gibt nicht garantiert die gleiche Menge an Token zurück, die zum Starten verwendet wurden der Prozess. –

Antwort

1

Vielen Dank an alle für die Vorschläge. Ich benutzte den Algorithmus von shquote (http://www.daemon-systems.org/man/shquote.3.html).

/** 
* Let's assume 'command' contains a collection of strings each of which is an 
* argument to our subprocess (it does not include arg0). 
*/ 
string args = ""; 
string curArg; 
foreach (String s in command) { 
    curArg = s.Replace("'", "'\\''"); // 1.) Replace ' with '\'' 
    curArg = "'"+curArg+"'";   // 2.) Surround with 's 
    // 3.) Is removal of unnecessary ' pairs. This is non-trivial and unecessary 
    args += " " + curArg; 
} 

Ich habe dies nur auf Linux getestet. Für Windows können Sie die Argumente einfach verketten.

0

Sie müssen einen neuen Subprozess mit grep und allen Argumenten, die grep benötigen, ausführen.

void runProcess(string processName, string args) 
{ 
    using (Process p = new Process()) 
    { 
     ProcessStartInfo info = new ProcessStartInfo(processName); 
     info.Arguments = args; 
     info.RedirectStandardInput = true; 
     info.RedirectStandardOutput = true; 
     info.UseShellExecute = false; 
     p.StartInfo = info; 
     p.Start(); 
     string output = p.StandardOutput.ReadToEnd(); 
     // process output 
    } 
} 

dann einen Anruf tätigen, um runProcess("grep", "a", "b", "c", "foo.txt");

Edit: Aktualisiert args Handhabung.

+2

Dies funktioniert nicht, wenn Argumente im Array 'args' Leerzeichen enthalten. Haben Sie von Ihrer Aufrufsyntax auch gemeint, 'args' als' params'-Array zu deklarieren? Weil das Beispiel, wie Sie es haben, nicht so verwendet werden kann, wie Sie es zeigen. –

+0

@Daniel Pryden Sie haben Recht bezüglich der Parameter, und ich nehme an, es wäre Aufgabe des Entwicklers, dieses Skript so zu modifizieren, dass es in seinem eigenen Code verwendet wird. Dieser Beitrag sollte eher als allgemeiner Leitfaden dafür gesehen werden, wie ein Subprozess gestartet wird, als ein voll funktionsfähiges Programm, das wörtlich in Ihr aktuelles System integriert wird. – Jake

0

Nur eine Regex verwenden, um zu überprüfen, ob ein Zeichenfolge Räume jeglicher Art hat, und die ursprüngliche Zeichenfolge durch einen neuen mit Anführungszeichen ersetzen:

using System.Text.RegularExpressions; 
// ... 
for(int i=0; i<argv.Length; i++) { 
    if (Regex.IsMatch(i, "(\s|\")+")) { 
     argv[i] = "\"" + argv[i] + "\""; 
     argv[i].Replace("\"", "\\\""); 
    } 
} 
+0

Dies funktioniert nicht, wenn der String Zitate enthält: zB "a \" b "erzeugt arg, a" b, was, wenn es von Anführungszeichen umgeben ist, "a" b "ist falsch und eine unzulässige Zeichenkette. – lucas

+0

Nun, dann müssen Sie nur die Regex erweitern und eine string.replace hinzufügen. – supercheetah

1

In windowsland, es ist wirklich einfach ... zusätzliches Zitat hinzufügen markiert in der Zeichenfolge, die Sie an das System.Diagnostics.ProcessStartInfo-Objekt übergeben.

z.B. "./my_commandline" "myarg1 myarg2 - grep" a b c "foo.txt"

+0

Sollte in allen Umgebungen funktionieren, ist es, was ich in cygwin verwenden. Stellen Sie sicher, dass Ihre Zeichenfolge tatsächlich maskiert Backslashes verwendet - also buchstäblich in Ihrem Code, du hättest @ "myarg1 myarg2 - grep \" "abc \" "foo.txt" oder "myarg1 myarg2 - grep \\\" abc \\\ "foo.txt". –