2010-06-02 4 views
7

Ho un'applicazione di riga di comando C# che devo eseguire in Windows e in mono in Unix. Ad un certo punto voglio avviare un sottoprocesso dato un insieme di parametri arbitrari passati attraverso la riga di comando. Per esempio:Come posso avviare un sottoprocesso in C# con un argv? (Oppure convertire agrv in una stringa di argomento legale)

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

Purtroppo, System.Diagnostics.ProcessStartInfo richiede solo una stringa per args. Questo è un problema per i comandi come:

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

In questo caso argv assomiglia:

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

Si noti che le virgolette intorno "abc" vengono eliminati dalla shell, quindi se ho semplicemente concatenare i argomenti al fine di creare la stringa arg per ProcessStartInfo ottengo:

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

che non è quello che voglio.

C'è un modo semplice per passare un argv al sottoprocesso di avvio in C# OR per convertire un argv arbitrario in una stringa che è legale per windows e shell di Linux?

Qualsiasi aiuto sarebbe molto apprezzato.

+1

Solo una nota: C# su Windows non presenta questo problema perché Windows non ha tecnicamente un argv. L'intera riga di comando viene passata al processo e la suddivisione degli argomenti è il lavoro del nuovo processo, non del sistema operativo.Unix ha l'approccio opposto: il sistema operativo è responsabile del passaggio di una serie di stringhe, pertanto elementi come l'espansione dei caratteri jolly possono essere gestiti dal processo di chiamata. –

+0

@DanielPryden che non è vero. i programmi Windows hanno una lista argv come quelli POSIX. .Net nasconde semplicemente questo elenco. Piuttosto irritante se me lo chiedi. – IanNorton

+0

@IanNorton: al contrario, 'CreateProcess' accetta solo una singola stringa di argomenti. Il Microsoft C Runtime (MSVCRT) analizza quella stringa e la rende disponibile come argv, ma i programmi non sono richiesti per utilizzare l'implementazione CRT e l'analisi eseguita dal CRT non garantisce il ritorno dello stesso insieme di token che sono stati utilizzati per il lancio il processo. –

risposta

1

Grazie a tutti per i suggerimenti. Ho finito per utilizzare l'algoritmo di 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; 
} 

L'ho provato solo su linux. Per Windows puoi semplicemente concatenare gli argomenti.

0

Sarà necessario eseguire un nuovo sottoprocesso utilizzando grep e tutti gli argomenti che saranno necessari 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 
    } 
} 

poi effettuare una chiamata a runProcess("grep", "a", "b", "c", "foo.txt");

Modifica: Aggiornato movimentazione args.

+2

Questo non funzionerà se qualsiasi argomento nell'array 'args' contiene spazi bianchi. Inoltre, dalla sintassi della tua chiamata, intendevi dichiarare 'args' come una matrice' params'? Perché l'esempio come lo hai non può essere usato come tu mostri. –

+0

@Daniel Pryden Hai ragione riguardo i param e suppongo che spetti allo sviluppatore modificare questo script al punto di usarlo nel suo codice. Questo post dovrebbe essere visto più come una guida generale su come avviare un sottoprocesso di un programma completamente funzionante per essere integrato testualmente nel sistema corrente. – Jake

0

semplicemente utilizzare un Regex per controllare se una stringa ha spazi di ogni tipo, e sostituire la stringa originale con uno nuovo con le citazioni:

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

Questo non funzionerà se la stringa ha delle virgolette: cioè "a \" b "produce l'arg, un" b, che se circondato da virgolette è "a" b "che è sia errato sia una stringa illegale – lucas

+0

Bene, allora hai solo bisogno di espandere la regex e aggiungere una stringa.replace. – supercheetah

1

In windowsland, è semplice, in realtà ... aggiungi citazione in più segna nella stringa che si passa all'oggetto System.Diagnostics.ProcessStartInfo.

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

+0

Dovrebbe funzionare bene in tutti gli ambienti, è quello che uso in cygwin.Assicurati che la tua stringa stia usando effettivamente barre rovesciate di escape - quindi, letteralmente nel tuo codice, tu avresti @ "myarg1 myarg2 - grep \" "abc \" "foo.txt" o "myarg1 myarg2 - grep \\\" abc \\\ "foo.txt". –