2010-06-02 5 views
7

Tengo una aplicación de línea de comandos C# que necesito ejecutar en Windows y en mono en Unix. En algún momento quiero iniciar un subproceso dado un conjunto de parámetros arbitrarios pasados ​​a través de la línea de comando. Por ejemplo:¿Cómo inicio un subproceso en C# con un argv? (O convierte agrv a una cadena de argumentos legal)

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

Desafortunadamente, System.Diagnostics.ProcessStartInfo sólo toma una cadena de argumentos. Este es un problema para los comandos como:

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

En este caso argv parece:

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

Tenga en cuenta que las comillas en "abc" son despojados por el shell por lo que si simplemente concatenar los argumentos para crear la cadena arg para ProcessStartInfo obtengo:

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

Que no es lo que quiero.

¿Hay una manera simple de pasar un argv al lanzamiento del subproceso en C# O para convertir un argv arbitrario en una cadena que es legal para Windows y linux shell?

Cualquier ayuda sería muy apreciada.

+1

Solo una nota: C# en Windows no tiene este problema porque Windows técnicamente no tiene un argv. Toda la línea de comando se pasa al proceso, y la división de los argumentos es el trabajo del nuevo proceso, no del sistema operativo.Unix adopta el enfoque opuesto: el sistema operativo es responsable de pasar una serie de cadenas, por lo que el proceso de llamada puede manejar cosas como la expansión de comodines. –

+0

@DanielPryden eso no es verdad. Los programas de Windows tienen una lista de argv como POSIX. .Net simplemente oculta esta lista. Bastante irritante si me preguntas. – IanNorton

+0

@IanNorton: Por el contrario, 'CreateProcess' solo toma una sola cadena de argumento. Microsoft C Runtime (MSVCRT) analiza esa cadena y la hace disponible como argv, pero no es necesario que los programas utilicen la implementación CRT, y el análisis que hace la CRT no garantiza que devuelva el mismo conjunto de tokens que se utilizaron para iniciarse. el proceso. –

Respuesta

1

Gracias a todos por las sugerencias. Terminé usando el algoritmo de 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; 
} 

sólo he probado esto en Linux. Para Windows, puedes concatenar los argumentos.

0

Tendrá que ejecutar un nuevo subproceso usando grep y todos los argumentos que necesitará grep.

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 
    } 
} 

continuación, realizar una llamada a runProcess("grep", "a", "b", "c", "foo.txt");

Editar: Actualización args manipulación.

+2

Esto no funcionará si algún argumento en la matriz 'args' contiene espacios en blanco. Además, desde su sintaxis de llamada, ¿quiso declarar 'args' como una matriz' params'? Porque el ejemplo tal como lo tienes no se puede usar de la manera que se muestra. –

+0

@Daniel Pryden Tiene razón acerca de los parámetros, y supongo que correspondería al desarrollador modificar esta secuencia de comandos hasta el punto de usarla en su propio código. Esta publicación debería verse más como una guía general sobre cómo iniciar un subproceso que un programa completamente funcional que se integrará textualmente en su sistema actual. – Jake

0

sólo tiene que utilizar una expresión regular para comprobar si una cadena tiene espacios de ningún tipo, y reemplazar la cadena original con uno nuevo con citas:

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

Esto no funcionará si la cadena tiene comillas en ella: es decir, "a \" b "produce la arg, a" b, que si está rodeada por comillas es "a" b "que es incorrecta y una cadena ilegal. – lucas

+0

Bueno, entonces solo necesitas expandir la expresión regular y agregar una cadena.sustituir. – supercheetah

1

En windowsland, es simple en realidad ... añadir cita adicionales marcas en la cadena que pasa al objeto System.Diagnostics.ProcessStartInfo.

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

+0

Debería funcionar bien en todos los entornos, es lo que uso en cygwin. Asegúrate de que la cadena esté usando barras diagonales vacías, así que, literalmente, en tu código, tendrías @ "myarg1 myarg2 - grep \" "abc \" "foo.txt" o "myarg1 myarg2 - grep \\\" abc \\\ "foo.txt". –

Cuestiones relacionadas